Skip to content

Commit

Permalink
feat(dhcp6): implement NTP_SERVER options (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
aurelilys authored Jan 15, 2024
1 parent a3c7602 commit c0e7e79
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/v6/option_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ impl From<&DhcpOption> for OptionCode {
O::IAPD(_) => OptionCode::IAPD,
O::IAPrefix(_) => OptionCode::IAPrefix,
O::InformationRefreshTime(_) => OptionCode::InformationRefreshTime,
O::NtpServer(_) => OptionCode::NtpServer,
// SolMaxRt(_) => OptionCode::SolMaxRt,
// InfMaxRt(_) => OptionCode::InfMaxRt,
// LqQuery(_) => OptionCode::LqQuery,
Expand Down
99 changes: 99 additions & 0 deletions src/v6/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ pub enum DhcpOption {
/// 26 - <https://datatracker.ietf.org/doc/html/rfc3633#section-10>
IAPrefix(IAPrefix),
InformationRefreshTime(u32),
/// 56 - <https://datatracker.ietf.org/doc/html/rfc5908>
NtpServer(Vec<NtpSuboption>),
// SolMaxRt(u32),
// InfMaxRt(u32),
// LqQuery(_),
Expand Down Expand Up @@ -486,6 +488,66 @@ impl Decodable for IAAddr {
}
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NtpSuboption {
ServerAddress(Ipv6Addr),
MulticastAddress(Ipv6Addr),
FQDN(Name),
}

impl Decodable for NtpSuboption {
fn decode(decoder: &mut Decoder<'_>) -> DecodeResult<Self> {
let code = decoder.read_u16()?;
match code {
1 | 2 => {
let len = decoder.read_u16()?;
if len != 16 {
return Err(super::DecodeError::NotEnoughBytes);
}
let addr: Ipv6Addr = decoder.read::<16>()?.into();
let option = if addr.is_multicast() {
NtpSuboption::MulticastAddress(addr)
} else {
NtpSuboption::ServerAddress(addr)
};
Ok(option)
}
3 => {
let len = decoder.read_u16()? as _;
let mut name_decoder = BinDecoder::new(decoder.read_slice(len)?);
Ok(NtpSuboption::FQDN(Name::read(&mut name_decoder)?))
}
_ => Err(super::DecodeError::IoError(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"invalid ntp suboption code",
))),
}
}
}

impl Encodable for NtpSuboption {
fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> {
match self {
NtpSuboption::ServerAddress(addr) | NtpSuboption::MulticastAddress(addr) => {
let code = if addr.is_multicast() { 2 } else { 1 };
e.write_u16(code)?;
e.write_u16(16)?;
e.write::<16>(addr.octets())?;
}
NtpSuboption::FQDN(name) => {
let mut buf = Vec::new();
let mut name_encoder = BinEncoder::new(&mut buf);
name.emit(&mut name_encoder)?;
e.write_u16(3)?;
e.write_u16(buf.len() as _)?;
e.write_slice(&buf)?;
}
}
Ok(())
}
}

/// fallback for options not yet implemented
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -616,6 +678,15 @@ impl Decodable for DhcpOption {

DhcpOption::DomainSearchList(names)
}
OptionCode::NtpServer => {
let mut dec = Decoder::new(decoder.read_slice(len)?);
let mut suboptions = Vec::new();
while !dec.buffer().is_empty() {
suboptions.push(NtpSuboption::decode(&mut dec)?);
}

DhcpOption::NtpServer(suboptions)
}
// not yet implemented
OptionCode::Unknown(code) => DhcpOption::Unknown(UnknownOption {
code,
Expand Down Expand Up @@ -803,6 +874,15 @@ impl Encodable for DhcpOption {
e.write_u16(4)?;
e.write_u32(*time)?;
}
DhcpOption::NtpServer(suboptions) => {
let mut buf = Vec::new();
let mut subopt_enc = Encoder::new(&mut buf);
for suboption in suboptions {
suboption.encode(&mut subopt_enc)?;
}
e.write_u16(buf.len() as _)?;
e.write_slice(&buf)?;
}
DhcpOption::Unknown(UnknownOption { data, .. }) => {
e.write_u16(data.len() as u16)?;
e.write_slice(data)?;
Expand Down Expand Up @@ -888,6 +968,8 @@ where

#[cfg(test)]
mod tests {
use std::str::FromStr;

use super::*;
#[test]
fn test_range_binsearch() {
Expand Down Expand Up @@ -949,6 +1031,16 @@ mod tests {
0x00, 0x10, // length 16
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x0a, 0x00, 0x00,
0x14, 0x42, 0xe2, 0xff, 0xfe, 0x17, 0x84, 0x1a, // IPv6 address
0x00, 0x38, // OPTION_NTP_SERVER
0x00, 0x2b, // length 43
0x00, 0x01, // NTP_SUBOPTION_SRV_ADDR
0x00, 0x10, // suboption-len 16
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x0a, 0x00, 0x00,
0x14, 0x42, 0xe2, 0xff, 0xfe, 0x17, 0x84, 0x1a, // IPv6 address
0x00, 0x03, // NTP_SUBOPTION_SRV_FQDN
0x00, 0x13, // suboption-len 19
0x01 ,0x33, 0x02, 0x64, 0x65, 0x04, 0x70, 0x6f, 0x6f, 0x6c,
0x03, 0x6e, 0x74, 0x70, 0x03, 0x6f, 0x72, 0x67, 0x00 // 3.de.pool.ntp.org.
];

let mut expected_opts = DhcpOptions::new();
Expand Down Expand Up @@ -981,6 +1073,13 @@ mod tests {
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x42, 0xe2, 0xff, 0xfe, 0x17,
0x84, 0x1a,
])]));
expected_opts.insert(DhcpOption::NtpServer(vec![
NtpSuboption::ServerAddress(Ipv6Addr::from([
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x42, 0xe2, 0xff, 0xfe, 0x17,
0x84, 0x1a,
])),
NtpSuboption::FQDN(Name::from_str("3.de.pool.ntp.org.").unwrap()),
]));

let opts = DhcpOptions::decode(&mut Decoder::new(&raw)).unwrap();

Expand Down

0 comments on commit c0e7e79

Please sign in to comment.