Skip to content

Commit

Permalink
Implemented status messages for ntpv5.
Browse files Browse the repository at this point in the history
Note: this is based on the current proposal from us, not yet part of the
draft.
  • Loading branch information
davidv1992 authored and rnijveld committed Nov 17, 2023
1 parent 320baa2 commit f9405df
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 14 deletions.
118 changes: 107 additions & 11 deletions ntp-proto/src/packet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,25 @@ impl<'a> NtpPacket<'a> {
mac: None,
},
#[cfg(feature = "ntpv5")]
NtpHeader::V5(_header) => todo!("NTPv5 does not have KISS codes yet"),
NtpHeader::V5(header) => NtpPacket {
header: NtpHeader::V5(v5::NtpHeaderV5::rate_limit_response(header)),
efdata: ExtensionFieldData {
authenticated: vec![],
encrypted: vec![],
// Ignore encrypted so as not to accidentaly leak anything
untrusted: packet_from_client
.efdata
.untrusted
.into_iter()
.chain(packet_from_client.efdata.authenticated)
.filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
.chain(std::iter::once(ExtensionField::DraftIdentification(
Cow::Borrowed(v5::DRAFT_VERSION),
)))
.collect(),
},
mac: None,
},
}
}

Expand All @@ -880,7 +898,23 @@ impl<'a> NtpPacket<'a> {
mac: None,
},
#[cfg(feature = "ntpv5")]
NtpHeader::V5(_header) => todo!("No NTS support yet"),
NtpHeader::V5(header) => NtpPacket {
header: NtpHeader::V5(v5::NtpHeaderV5::rate_limit_response(header)),
efdata: ExtensionFieldData {
authenticated: packet_from_client
.efdata
.authenticated
.into_iter()
.filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
.chain(std::iter::once(ExtensionField::DraftIdentification(
Cow::Borrowed(v5::DRAFT_VERSION),
)))
.collect(),
encrypted: vec![],
untrusted: vec![],
},
mac: None,
},
}
}

Expand Down Expand Up @@ -908,7 +942,25 @@ impl<'a> NtpPacket<'a> {
mac: None,
},
#[cfg(feature = "ntpv5")]
NtpHeader::V5(_header) => todo!("NTPv5 does not have KISS codes yet"),
NtpHeader::V5(header) => NtpPacket {
header: NtpHeader::V5(v5::NtpHeaderV5::deny_response(header)),
efdata: ExtensionFieldData {
authenticated: vec![],
encrypted: vec![],
// Ignore encrypted so as not to accidentaly leak anything
untrusted: packet_from_client
.efdata
.untrusted
.into_iter()
.chain(packet_from_client.efdata.authenticated)
.filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
.chain(std::iter::once(ExtensionField::DraftIdentification(
Cow::Borrowed(v5::DRAFT_VERSION),
)))
.collect(),
},
mac: None,
},
}
}

Expand All @@ -930,7 +982,23 @@ impl<'a> NtpPacket<'a> {
mac: None,
},
#[cfg(feature = "ntpv5")]
NtpHeader::V5(_header) => todo!("No NTS support for NTPv5 yet"),
NtpHeader::V5(header) => NtpPacket {
header: NtpHeader::V5(v5::NtpHeaderV5::deny_response(header)),
efdata: ExtensionFieldData {
authenticated: packet_from_client
.efdata
.authenticated
.into_iter()
.filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
.chain(std::iter::once(ExtensionField::DraftIdentification(
Cow::Borrowed(v5::DRAFT_VERSION),
)))
.collect(),
encrypted: vec![],
untrusted: vec![],
},
mac: None,
},
}
}

Expand All @@ -953,7 +1021,24 @@ impl<'a> NtpPacket<'a> {
mac: None,
},
#[cfg(feature = "ntpv5")]
NtpHeader::V5(_header) => todo!("No NTS support for NTPv5 yet"),
NtpHeader::V5(header) => NtpPacket {
header: NtpHeader::V5(v5::NtpHeaderV5::nts_nak_response(header)),
efdata: ExtensionFieldData {
authenticated: vec![],
encrypted: vec![],
untrusted: packet_from_client
.efdata
.untrusted
.into_iter()
.chain(packet_from_client.efdata.authenticated)
.filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
.chain(std::iter::once(ExtensionField::DraftIdentification(
Cow::Borrowed(v5::DRAFT_VERSION),
)))
.collect(),
},
mac: None,
},
}
}
}
Expand Down Expand Up @@ -1074,30 +1159,41 @@ impl<'a> NtpPacket<'a> {
}
}

fn kiss_code(&self) -> ReferenceId {
match self.header {
NtpHeader::V3(header) => header.reference_id,
NtpHeader::V4(header) => header.reference_id,
#[cfg(feature = "ntpv5")]
// Kiss code in ntpv5 is the first four bytes of the server cookie
NtpHeader::V5(header) => {
ReferenceId::from_bytes(header.server_cookie.0[..4].try_into().unwrap())
}
}
}

pub fn is_kiss(&self) -> bool {
match self.header {
NtpHeader::V3(header) => header.stratum == 0,
NtpHeader::V4(header) => header.stratum == 0,
#[cfg(feature = "ntpv5")]
// TODO NTPv5 does not have Kiss codes so we pretend everything is always fine
NtpHeader::V5(_header) => false,
NtpHeader::V5(header) => header.flags.status_message,
}
}

pub fn is_kiss_deny(&self) -> bool {
self.is_kiss() && self.reference_id().is_deny()
self.is_kiss() && self.kiss_code().is_deny()
}

pub fn is_kiss_rate(&self) -> bool {
self.is_kiss() && self.reference_id().is_rate()
self.is_kiss() && self.kiss_code().is_rate()
}

pub fn is_kiss_rstr(&self) -> bool {
self.is_kiss() && self.reference_id().is_rstr()
self.is_kiss() && self.kiss_code().is_rstr()
}

pub fn is_kiss_ntsn(&self) -> bool {
self.is_kiss() && self.reference_id().is_ntsn()
self.is_kiss() && self.kiss_code().is_ntsn()
}

#[cfg(feature = "ntpv5")]
Expand Down
44 changes: 41 additions & 3 deletions ntp-proto/src/packet/v5/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,21 @@ pub struct NtpEra(pub u8);

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NtpFlags {
unknown_leap: bool,
interleaved_mode: bool,
pub unknown_leap: bool,
pub interleaved_mode: bool,
pub status_message: bool,
}

impl NtpFlags {
const fn from_bits(bits: [u8; 2]) -> Result<Self, ParsingError<std::convert::Infallible>> {
if bits[0] != 0x00 || bits[1] & 0b1111_1100 != 0 {
if bits[0] != 0x00 || bits[1] & 0b1111_1000 != 0 {
return Err(V5Error::InvalidFlags.into_parse_err());
}

Ok(Self {
unknown_leap: bits[1] & 0b01 != 0,
interleaved_mode: bits[1] & 0b10 != 0,
status_message: bits[1] & 0b100 != 0,
})
}

Expand All @@ -104,6 +106,10 @@ impl NtpFlags {
flags |= 0b10;
}

if self.status_message {
flags |= 0b100;
}

[0x00, flags]
}
}
Expand Down Expand Up @@ -173,6 +179,7 @@ impl NtpHeaderV5 {
flags: NtpFlags {
unknown_leap: false,
interleaved_mode: false,
status_message: false,
},
server_cookie: NtpServerCookie([0; 8]),
client_cookie: NtpClientCookie([0; 8]),
Expand Down Expand Up @@ -200,6 +207,7 @@ impl NtpHeaderV5 {
flags: NtpFlags {
unknown_leap: false,
interleaved_mode: false,
status_message: false,
},
root_delay: system.time_snapshot.root_delay,
root_dispersion: system.time_snapshot.root_dispersion,
Expand All @@ -210,6 +218,35 @@ impl NtpHeaderV5 {
}
}

fn kiss_response(packet_from_client: Self, code: [u8; 4]) -> Self {
Self {
mode: NtpMode::Response,
flags: NtpFlags {
unknown_leap: false,
interleaved_mode: false,
status_message: true,
},
server_cookie: NtpServerCookie([code[0], code[1], code[2], code[3], 0, 0, 0, 0]),
client_cookie: packet_from_client.client_cookie,
..Self::new()
}
}

pub(crate) fn rate_limit_response(packet_from_client: Self) -> Self {
Self {
poll: packet_from_client.poll.force_inc(),
..Self::kiss_response(packet_from_client, *b"RATE")
}
}

pub(crate) fn deny_response(packet_from_client: Self) -> Self {
Self::kiss_response(packet_from_client, *b"DENY")
}

pub(crate) fn nts_nak_response(packet_from_client: Self) -> Self {
Self::kiss_response(packet_from_client, *b"NTSN")
}

const WIRE_LENGTH: usize = 48;
const VERSION: u8 = 5;

Expand Down Expand Up @@ -476,6 +513,7 @@ mod tests {
flags: NtpFlags {
unknown_leap: i % 3 == 0,
interleaved_mode: i % 4 == 0,
status_message: i % 5 == 0,
},
root_delay: NtpDuration::from_bits_short([i; 4]),
root_dispersion: NtpDuration::from_bits_short([i.wrapping_add(1); 4]),
Expand Down
5 changes: 5 additions & 0 deletions ntp-proto/src/time_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,11 @@ impl PollInterval {
Self(self.0 + 1).min(limits.max)
}

#[must_use]
pub fn force_inc(self) -> Self {
Self(self.0.saturating_add(1))
}

#[must_use]
pub fn dec(self, limits: PollIntervalLimits) -> Self {
Self(self.0 - 1).max(limits.min)
Expand Down

0 comments on commit f9405df

Please sign in to comment.