use std::io::Cursor;
use crate::{
cookiestash::CookieStash,
packet::{Cipher, NtpAssociationMode, RequestIdentifier},
time_types::NtpInstant,
NtpDuration, NtpPacket, NtpTimestamp, PollInterval, ReferenceId, SystemConfig, SystemSnapshot,
};
use serde::{Deserialize, Serialize};
use tracing::{debug, info, instrument, trace, warn};
const MAX_STRATUM: u8 = 16;
const POLL_WINDOW: std::time::Duration = std::time::Duration::from_secs(5);
const STARTUP_TRIES_THRESHOLD: usize = 3;
#[derive(Debug, thiserror::Error)]
pub enum NtsError {
#[error("Ran out of nts cookies")]
OutOfCookies,
}
pub struct PeerNtsData {
pub(crate) cookies: CookieStash,
pub(crate) c2s: Box<dyn Cipher>,
pub(crate) s2c: Box<dyn Cipher>,
}
#[cfg(feature = "ext-test")]
impl PeerNtsData {
pub fn get_cookie(&mut self) -> Option<Vec<u8>> {
self.cookies.get()
}
pub fn get_keys(self) -> (Box<dyn Cipher>, Box<dyn Cipher>) {
(self.c2s, self.s2c)
}
}
impl std::fmt::Debug for PeerNtsData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PeerNtsData")
.field("cookies", &self.cookies)
.finish()
}
}
#[derive(Debug)]
pub struct Peer {
nts: Option<Box<PeerNtsData>>,
backoff_interval: PollInterval,
last_poll_interval: PollInterval,
remote_min_poll_interval: PollInterval,
current_request_identifier: Option<(RequestIdentifier, NtpInstant)>,
stratum: u8,
reference_id: ReferenceId,
peer_id: ReferenceId,
our_id: ReferenceId,
reach: Reach,
tries: usize,
system_config: SystemConfig,
}
#[derive(Debug, Copy, Clone)]
pub struct Measurement {
pub delay: NtpDuration,
pub offset: NtpDuration,
pub localtime: NtpTimestamp,
pub monotime: NtpInstant,
}
impl Measurement {
fn from_packet(
packet: &NtpPacket,
send_timestamp: NtpTimestamp,
recv_timestamp: NtpTimestamp,
local_clock_time: NtpInstant,
precision: NtpDuration,
) -> Self {
Self {
delay: ((recv_timestamp - send_timestamp)
- (packet.transmit_timestamp() - packet.receive_timestamp()))
.max(precision),
offset: ((packet.receive_timestamp() - send_timestamp)
+ (packet.transmit_timestamp() - recv_timestamp))
/ 2,
localtime: send_timestamp + (recv_timestamp - send_timestamp) / 2,
monotime: local_clock_time,
}
}
}
#[derive(Default, Clone, Copy, Serialize, Deserialize)]
pub struct Reach(u8);
impl std::fmt::Debug for Reach {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_reachable() {
write!(
f,
"Reach(0b{:07b} ({} polls until unreachable))",
self.0,
7 - self.0.trailing_zeros()
)
} else {
write!(f, "Reach(unreachable)",)
}
}
}
impl Reach {
pub fn is_reachable(&self) -> bool {
self.0 != 0
}
pub(crate) fn received_packet(&mut self) {
self.0 |= 1;
}
fn poll(&mut self) {
self.0 <<= 1
}
pub fn unanswered_polls(&self) -> u32 {
self.0.leading_zeros()
}
pub fn reachability_score(&self) -> u32 {
8 - self.0.trailing_zeros()
}
}
#[derive(Debug)]
pub enum IgnoreReason {
InvalidPacket,
InvalidMode,
InvalidVersion,
InvalidStratum,
InvalidPacketTime,
KissIgnore,
KissDemobilize,
KissNtsNack,
TooOld,
}
#[derive(Debug, Clone, Copy)]
pub struct PeerSnapshot {
pub peer_id: ReferenceId,
pub our_id: ReferenceId,
pub poll_interval: PollInterval,
pub reach: Reach,
pub stratum: u8,
pub reference_id: ReferenceId,
}
impl PeerSnapshot {
pub fn accept_synchronization(
&self,
local_stratum: u8,
) -> Result<(), AcceptSynchronizationError> {
use AcceptSynchronizationError::*;
if self.stratum >= local_stratum {
warn!(
stratum = debug(self.stratum),
"Peer rejected due to invalid stratum"
);
return Err(Stratum);
}
if self.stratum != 1 && self.reference_id == self.our_id {
debug!("Peer rejected because of detected synchornization loop");
return Err(Loop);
}
if !self.reach.is_reachable() {
warn!("Peer unreachable");
return Err(ServerUnreachable);
}
Ok(())
}
pub fn from_peer(peer: &Peer) -> Self {
Self {
peer_id: peer.peer_id,
our_id: peer.our_id,
stratum: peer.stratum,
reference_id: peer.reference_id,
reach: peer.reach,
poll_interval: peer.last_poll_interval,
}
}
}
#[cfg(feature = "ext-test")]
pub fn peer_snapshot() -> crate::PeerSnapshot {
let mut reach = crate::peer::Reach::default();
reach.received_packet();
crate::PeerSnapshot {
peer_id: ReferenceId::from_int(0),
stratum: 0,
reference_id: ReferenceId::from_int(0),
our_id: ReferenceId::from_int(1),
reach,
poll_interval: crate::time_types::PollIntervalLimits::default().min,
}
}
#[derive(Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum AcceptSynchronizationError {
ServerUnreachable,
Loop,
Distance,
Stratum,
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum Update {
BareUpdate(PeerSnapshot),
NewMeasurement(PeerSnapshot, Measurement, NtpPacket<'static>),
}
#[derive(Debug, thiserror::Error)]
pub enum PollError {
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("peer unreachable")]
Unreachable,
}
impl Peer {
#[instrument]
pub fn new(
our_id: ReferenceId,
peer_id: ReferenceId,
local_clock_time: NtpInstant,
system_config: SystemConfig,
) -> Self {
Self {
nts: None,
last_poll_interval: system_config.poll_limits.min,
backoff_interval: system_config.poll_limits.min,
remote_min_poll_interval: system_config.poll_limits.min,
current_request_identifier: None,
our_id,
peer_id,
reach: Default::default(),
tries: 0,
stratum: 16,
reference_id: ReferenceId::NONE,
system_config,
}
}
#[instrument]
pub fn new_nts(
our_id: ReferenceId,
peer_id: ReferenceId,
local_clock_time: NtpInstant,
system_config: SystemConfig,
nts: Box<PeerNtsData>,
) -> Self {
Self {
nts: Some(nts),
..Self::new(our_id, peer_id, local_clock_time, system_config)
}
}
pub fn update_config(&mut self, system_config: SystemConfig) {
self.system_config = system_config;
}
pub fn current_poll_interval(&self, system: SystemSnapshot) -> PollInterval {
system
.time_snapshot
.poll_interval
.max(self.backoff_interval)
.max(self.remote_min_poll_interval)
}
pub fn generate_poll_message<'a>(
&mut self,
buf: &'a mut [u8],
system: SystemSnapshot,
system_config: &SystemConfig,
) -> Result<&'a [u8], PollError> {
if !self.reach.is_reachable() && self.tries >= STARTUP_TRIES_THRESHOLD {
return Err(PollError::Unreachable);
}
self.reach.poll();
self.tries = self.tries.saturating_add(1);
let poll_interval = self.current_poll_interval(system);
let (packet, identifier) = match &mut self.nts {
Some(nts) => {
let cookie = nts.cookies.get().ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::Other, NtsError::OutOfCookies)
})?;
NtpPacket::nts_poll_message(&cookie, nts.cookies.gap(), poll_interval)
}
None => NtpPacket::poll_message(poll_interval),
};
self.current_request_identifier = Some((identifier, NtpInstant::now() + POLL_WINDOW));
self.backoff_interval = poll_interval.inc(system_config.poll_limits);
let mut cursor = Cursor::new(buf);
packet.serialize(&mut cursor, &self.nts.as_ref().map(|nts| nts.c2s.as_ref()))?;
let used = cursor.position();
let result = &cursor.into_inner()[..used as usize];
Ok(result)
}
#[instrument(skip(self, system), fields(peer = debug(self.peer_id)))]
pub fn handle_incoming(
&mut self,
system: SystemSnapshot,
message: &[u8],
local_clock_time: NtpInstant,
send_time: NtpTimestamp,
recv_time: NtpTimestamp,
) -> Result<Update, IgnoreReason> {
let message =
match NtpPacket::deserialize(message, &self.nts.as_ref().map(|nts| nts.s2c.as_ref())) {
Ok((packet, _)) => packet,
Err(e) => {
warn!("received invalid packet: {}", e);
return Err(IgnoreReason::InvalidPacket);
}
};
let request_identifier = match self.current_request_identifier {
Some((next_expected_origin, validity)) if validity >= NtpInstant::now() => {
next_expected_origin
}
_ => {
debug!("Received old/unexpected packet from peer");
return Err(IgnoreReason::InvalidPacketTime);
}
};
if !message.valid_server_response(request_identifier, self.nts.is_some()) {
debug!("Received old/unexpected packet from peer");
Err(IgnoreReason::InvalidPacketTime)
} else if message.is_kiss_rate() {
self.remote_min_poll_interval = Ord::max(
self.remote_min_poll_interval
.inc(self.system_config.poll_limits),
self.last_poll_interval,
);
warn!(?self.remote_min_poll_interval, "Peer requested rate limit");
Err(IgnoreReason::KissIgnore)
} else if message.is_kiss_rstr() || message.is_kiss_deny() {
warn!("Peer denied service");
Err(IgnoreReason::KissDemobilize)
} else if message.is_kiss_ntsn() {
warn!("Received nts not-acknowledge");
self.backoff_interval = self.system_config.poll_limits.min;
Err(IgnoreReason::KissNtsNack)
} else if message.is_kiss() {
warn!("Unrecognized KISS Message from peer");
Err(IgnoreReason::KissIgnore)
} else if message.stratum() > MAX_STRATUM {
warn!(
"Received message from server with excessive stratum {}",
message.stratum()
);
Err(IgnoreReason::InvalidStratum)
} else if message.mode() != NtpAssociationMode::Server {
warn!("Received packet with invalid mode");
Err(IgnoreReason::InvalidMode)
} else {
Ok(self.process_message(system, message, local_clock_time, send_time, recv_time))
}
}
#[allow(clippy::too_many_arguments)]
fn process_message(
&mut self,
system: SystemSnapshot,
message: NtpPacket,
local_clock_time: NtpInstant,
send_time: NtpTimestamp,
recv_time: NtpTimestamp,
) -> Update {
trace!("Packet accepted for processing");
self.reach.received_packet();
self.backoff_interval = self.system_config.poll_limits.min;
self.current_request_identifier = None;
self.stratum = message.stratum();
self.reference_id = message.reference_id();
let measurement = Measurement::from_packet(
&message,
send_time,
recv_time,
local_clock_time,
system.time_snapshot.precision,
);
if let Some(nts) = self.nts.as_mut() {
for cookie in message.new_cookies() {
nts.cookies.store(cookie);
}
}
Update::NewMeasurement(
PeerSnapshot::from_peer(self),
measurement,
message.into_owned(),
)
}
#[instrument(level="trace", skip(self), fields(peer = debug(self.peer_id)))]
pub fn reset(&mut self) {
self.current_request_identifier = None;
info!(our_id = ?self.our_id, peer_id = ?self.peer_id, "Peer reset");
}
#[cfg(test)]
pub(crate) fn test_peer() -> Self {
Peer {
nts: None,
last_poll_interval: PollInterval::default(),
backoff_interval: PollInterval::default(),
remote_min_poll_interval: PollInterval::default(),
current_request_identifier: None,
peer_id: ReferenceId::from_int(0),
our_id: ReferenceId::from_int(0),
reach: Reach::default(),
tries: 0,
stratum: 0,
reference_id: ReferenceId::from_int(0),
system_config: SystemConfig::default(),
}
}
}
#[cfg(feature = "fuzz")]
pub fn fuzz_measurement_from_packet(
client: u64,
client_interval: u32,
server: u64,
server_interval: u32,
client_precision: i8,
server_precision: i8,
) {
let mut packet = NtpPacket::test();
packet.set_origin_timestamp(NtpTimestamp::from_fixed_int(client));
packet.set_receive_timestamp(NtpTimestamp::from_fixed_int(server));
packet.set_transmit_timestamp(NtpTimestamp::from_fixed_int(
server.wrapping_add(server_interval as u64),
));
packet.set_precision(server_precision);
let result = Measurement::from_packet(
&packet,
NtpTimestamp::from_fixed_int(client),
NtpTimestamp::from_fixed_int(client.wrapping_add(client_interval as u64)),
NtpInstant::now(),
NtpDuration::from_exponent(client_precision),
);
assert!(result.delay >= NtpDuration::ZERO);
}
#[cfg(test)]
mod test {
use crate::{packet::NoCipher, time_types::PollIntervalLimits};
use super::*;
use std::time::Duration;
#[test]
fn test_measurement_from_packet() {
let instant = NtpInstant::now();
let mut packet = NtpPacket::test();
packet.set_receive_timestamp(NtpTimestamp::from_fixed_int(1));
packet.set_transmit_timestamp(NtpTimestamp::from_fixed_int(2));
let result = Measurement::from_packet(
&packet,
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(3),
instant,
NtpDuration::from_exponent(-32),
);
assert_eq!(result.offset, NtpDuration::from_fixed_int(0));
assert_eq!(result.delay, NtpDuration::from_fixed_int(2));
packet.set_receive_timestamp(NtpTimestamp::from_fixed_int(2));
packet.set_transmit_timestamp(NtpTimestamp::from_fixed_int(3));
let result = Measurement::from_packet(
&packet,
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(3),
instant,
NtpDuration::from_exponent(-32),
);
assert_eq!(result.offset, NtpDuration::from_fixed_int(1));
assert_eq!(result.delay, NtpDuration::from_fixed_int(2));
packet.set_receive_timestamp(NtpTimestamp::from_fixed_int(0));
packet.set_transmit_timestamp(NtpTimestamp::from_fixed_int(5));
let result = Measurement::from_packet(
&packet,
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(3),
instant,
NtpDuration::from_exponent(-32),
);
assert_eq!(result.offset, NtpDuration::from_fixed_int(1));
assert_eq!(result.delay, NtpDuration::from_fixed_int(1));
}
#[test]
fn reachability() {
let mut reach = Reach::default();
assert!(!reach.is_reachable());
reach.received_packet();
assert!(reach.is_reachable());
for _ in 0..7 {
reach.poll();
}
assert!(reach.is_reachable());
reach.poll();
assert!(!reach.is_reachable());
reach.received_packet();
assert!(reach.is_reachable());
}
#[test]
fn test_accept_synchronization() {
use AcceptSynchronizationError::*;
let mut peer = Peer::test_peer();
macro_rules! accept {
() => {{
let snapshot = PeerSnapshot::from_peer(&peer);
snapshot.accept_synchronization(16)
}};
}
assert_eq!(accept!(), Err(Loop));
peer.our_id = ReferenceId::from_int(42);
assert_eq!(accept!(), Err(ServerUnreachable));
peer.reach.received_packet();
assert_eq!(accept!(), Ok(()));
peer.stratum = 42;
assert_eq!(accept!(), Err(Stratum));
}
#[test]
fn test_poll_interval() {
let base = NtpInstant::now();
let mut peer = Peer::test_peer();
let mut system = SystemSnapshot::default();
assert!(peer.current_poll_interval(system) >= peer.remote_min_poll_interval);
assert!(peer.current_poll_interval(system) >= system.time_snapshot.poll_interval);
system.time_snapshot.poll_interval = PollIntervalLimits::default().max;
assert!(peer.current_poll_interval(system) >= peer.remote_min_poll_interval);
assert!(peer.current_poll_interval(system) >= system.time_snapshot.poll_interval);
system.time_snapshot.poll_interval = PollIntervalLimits::default().min;
peer.remote_min_poll_interval = PollIntervalLimits::default().max;
assert!(peer.current_poll_interval(system) >= peer.remote_min_poll_interval);
assert!(peer.current_poll_interval(system) >= system.time_snapshot.poll_interval);
peer.remote_min_poll_interval = PollIntervalLimits::default().min;
let prev = peer.current_poll_interval(system);
let mut buf = [0; 1024];
let packetbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let packet = NtpPacket::deserialize(packetbuf, &NoCipher).unwrap().0;
assert!(peer.current_poll_interval(system) > prev);
let mut response = NtpPacket::test();
response.set_mode(NtpAssociationMode::Server);
response.set_stratum(1);
response.set_origin_timestamp(packet.transmit_timestamp());
assert!(peer
.handle_incoming(
system,
&response.serialize_without_encryption_vec().unwrap(),
base,
NtpTimestamp::default(),
NtpTimestamp::default()
)
.is_ok());
assert_eq!(peer.current_poll_interval(system), prev);
let prev = peer.current_poll_interval(system);
let mut buf = [0; 1024];
let packetbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let packet = NtpPacket::deserialize(packetbuf, &NoCipher).unwrap().0;
assert!(peer.current_poll_interval(system) > prev);
let mut response = NtpPacket::test();
response.set_mode(NtpAssociationMode::Server);
response.set_stratum(0);
response.set_origin_timestamp(packet.transmit_timestamp());
response.set_reference_id(ReferenceId::KISS_RATE);
assert!(peer
.handle_incoming(
system,
&response.serialize_without_encryption_vec().unwrap(),
base,
NtpTimestamp::default(),
NtpTimestamp::default()
)
.is_err());
assert!(peer.current_poll_interval(system) > prev);
assert!(peer.remote_min_poll_interval > prev);
}
#[test]
fn test_handle_incoming() {
let base = NtpInstant::now();
let mut peer = Peer::test_peer();
let system = SystemSnapshot::default();
let mut buf = [0; 1024];
let outgoingbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let outgoing = NtpPacket::deserialize(outgoingbuf, &NoCipher).unwrap().0;
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
packet.set_stratum(1);
packet.set_mode(NtpAssociationMode::Server);
packet.set_origin_timestamp(outgoing.transmit_timestamp());
packet.set_receive_timestamp(NtpTimestamp::from_fixed_int(100));
packet.set_transmit_timestamp(NtpTimestamp::from_fixed_int(200));
assert!(peer
.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(400)
)
.is_ok());
assert!(peer
.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(500)
)
.is_err());
}
#[test]
fn test_startup_unreachable() {
let mut peer = Peer::test_peer();
let system = SystemSnapshot::default();
let mut buf = [0; 1024];
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(matches!(
peer.generate_poll_message(&mut buf, system, &SystemConfig::default()),
Err(PollError::Unreachable)
));
}
#[test]
fn test_running_unreachable() {
let base = NtpInstant::now();
let mut peer = Peer::test_peer();
let system = SystemSnapshot::default();
let mut buf = [0; 1024];
let outgoingbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let outgoing = NtpPacket::deserialize(outgoingbuf, &NoCipher).unwrap().0;
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
packet.set_stratum(1);
packet.set_mode(NtpAssociationMode::Server);
packet.set_origin_timestamp(outgoing.transmit_timestamp());
packet.set_receive_timestamp(NtpTimestamp::from_fixed_int(100));
packet.set_transmit_timestamp(NtpTimestamp::from_fixed_int(200));
assert!(peer
.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(400)
)
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.is_ok());
assert!(matches!(
peer.generate_poll_message(&mut buf, system, &SystemConfig::default()),
Err(PollError::Unreachable)
));
}
#[test]
fn test_stratum_checks() {
let base = NtpInstant::now();
let mut peer = Peer::test_peer();
let system = SystemSnapshot::default();
let mut buf = [0; 1024];
let outgoingbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let outgoing = NtpPacket::deserialize(outgoingbuf, &NoCipher).unwrap().0;
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
packet.set_stratum(MAX_STRATUM + 1);
packet.set_mode(NtpAssociationMode::Server);
packet.set_origin_timestamp(outgoing.transmit_timestamp());
packet.set_receive_timestamp(NtpTimestamp::from_fixed_int(100));
packet.set_transmit_timestamp(NtpTimestamp::from_fixed_int(200));
assert!(peer
.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(500)
)
.is_err());
packet.set_stratum(0);
assert!(peer
.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(500)
)
.is_err());
}
#[test]
fn test_handle_kod() {
let base = NtpInstant::now();
let mut peer = Peer::test_peer();
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
packet.set_reference_id(ReferenceId::KISS_RSTR);
packet.set_mode(NtpAssociationMode::Server);
assert!(!matches!(
peer.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(100)
),
Err(IgnoreReason::KissDemobilize)
));
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
let mut buf = [0; 1024];
let outgoingbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let outgoing = NtpPacket::deserialize(outgoingbuf, &NoCipher).unwrap().0;
packet.set_reference_id(ReferenceId::KISS_RSTR);
packet.set_origin_timestamp(outgoing.transmit_timestamp());
packet.set_mode(NtpAssociationMode::Server);
assert!(matches!(
peer.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(100)
),
Err(IgnoreReason::KissDemobilize)
));
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
packet.set_reference_id(ReferenceId::KISS_DENY);
packet.set_mode(NtpAssociationMode::Server);
assert!(!matches!(
peer.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(100)
),
Err(IgnoreReason::KissDemobilize)
));
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
let outgoingbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let outgoing = NtpPacket::deserialize(outgoingbuf, &NoCipher).unwrap().0;
packet.set_reference_id(ReferenceId::KISS_DENY);
packet.set_origin_timestamp(outgoing.transmit_timestamp());
packet.set_mode(NtpAssociationMode::Server);
assert!(matches!(
peer.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(100)
),
Err(IgnoreReason::KissDemobilize)
));
let old_poll_interval = peer.last_poll_interval;
let old_remote_interval = peer.remote_min_poll_interval;
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
packet.set_reference_id(ReferenceId::KISS_RATE);
packet.set_mode(NtpAssociationMode::Server);
assert!(peer
.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(100)
)
.is_err());
assert_eq!(peer.remote_min_poll_interval, old_poll_interval);
assert_eq!(peer.remote_min_poll_interval, old_remote_interval);
let old_poll_interval = peer.last_poll_interval;
let old_remote_interval = peer.remote_min_poll_interval;
let mut packet = NtpPacket::test();
let system = SystemSnapshot::default();
let mut buf = [0; 1024];
let outgoingbuf = peer
.generate_poll_message(&mut buf, system, &SystemConfig::default())
.unwrap();
let outgoing = NtpPacket::deserialize(outgoingbuf, &NoCipher).unwrap().0;
packet.set_reference_id(ReferenceId::KISS_RATE);
packet.set_origin_timestamp(outgoing.transmit_timestamp());
packet.set_mode(NtpAssociationMode::Server);
assert!(peer
.handle_incoming(
system,
&packet.serialize_without_encryption_vec().unwrap(),
base + Duration::from_secs(1),
NtpTimestamp::from_fixed_int(0),
NtpTimestamp::from_fixed_int(100)
)
.is_err());
assert!(peer.remote_min_poll_interval > old_poll_interval);
assert!(peer.remote_min_poll_interval >= old_remote_interval);
}
}