1use std::{cmp::Ordering, collections::HashMap, net::Ipv6Addr};
4
5use crate::{
6 Buffer, BufferMut, DhcpError, DhcpV6Duid, DhcpV6OptionIaAddr,
7 DhcpV6OptionIaNa, DhcpV6OptionIaPd, DhcpV6OptionIaPrefix, DhcpV6OptionIaTa,
8 DhcpV6OptionStatus, ErrorContext, ErrorKind,
9};
10
11#[derive(Debug, PartialEq, Eq, Clone, Default)]
12pub(crate) struct DhcpV6Options {
13 data: HashMap<DhcpV6OptionCode, Vec<DhcpV6Option>>,
14}
15
16impl DhcpV6Options {
17 pub(crate) fn new() -> Self {
18 Self::default()
19 }
20
21 pub(crate) fn get_data_raw(&self, code: u16) -> Option<Vec<Vec<u8>>> {
22 let mut ret: Vec<Vec<u8>> = Vec::new();
23 if let Some(opts) = self.data.get(&DhcpV6OptionCode::from(code)) {
24 for opt in opts {
25 let mut buf = BufferMut::new();
26 opt.emit(&mut buf);
27 ret.push(buf.data);
28 }
29 Some(ret)
30 } else {
31 None
32 }
33 }
34
35 pub(crate) fn get_first(
36 &self,
37 code: DhcpV6OptionCode,
38 ) -> Option<&DhcpV6Option> {
39 self.data
40 .get(&code)
41 .map(|opts| opts.as_slice())
42 .and_then(|opts| opts.first())
43 }
44
45 pub(crate) fn insert(&mut self, opt: DhcpV6Option) {
46 self.data.entry(opt.code()).or_default().push(opt);
47 }
48
49 pub(crate) fn remove(&mut self, code: DhcpV6OptionCode) {
50 self.data.remove(&code);
51 }
52
53 pub(crate) fn parse(buf: &mut Buffer) -> Result<Self, DhcpError> {
54 let mut ret = Self::new();
55 while !buf.is_empty() {
56 match DhcpV6Option::parse(buf) {
57 Ok(opt) => {
58 ret.insert(opt);
59 }
60 Err(e) => {
61 log::info!(
62 "Ignore DHCPv6 option due to parsing error: {e}"
63 );
64 continue;
65 }
66 }
67 }
68 Ok(ret)
69 }
70
71 pub(crate) fn emit(&self, buf: &mut BufferMut) {
72 let mut all_opts: Vec<&DhcpV6Option> = Vec::new();
73
74 for opts in self.data.values() {
75 for opt in opts {
76 all_opts.push(opt);
77 }
78 }
79
80 all_opts.sort_unstable();
81
82 for opt in all_opts {
83 opt.emit(buf);
84 }
85 }
86}
87
88const OPTION_CLIENTID: u16 = 1;
89const OPTION_SERVERID: u16 = 2;
90const OPTION_IA_NA: u16 = 3;
91const OPTION_IA_TA: u16 = 4;
92const OPTION_IAADDR: u16 = 5;
93const OPTION_ORO: u16 = 6;
94const OPTION_PREFERENCE: u16 = 7;
95const OPTION_ELAPSED_TIME: u16 = 8;
96const OPTION_UNICAST: u16 = 12;
97const OPTION_STATUS_CODE: u16 = 13;
98const OPTION_RAPID_COMMIT: u16 = 14;
99const OPTION_DNS_SERVERS: u16 = 23;
100const OPTION_DOMAIN_LIST: u16 = 24;
101const OPTION_IA_PD: u16 = 25;
102const OPTION_IAPREFIX: u16 = 26;
103const OPTION_NTP_SERVER: u16 = 56;
104
105#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
106#[non_exhaustive]
107pub enum DhcpV6OptionCode {
108 #[default]
109 ClientId,
110 ServerId,
111 IANA,
112 IATA,
113 IAPD,
114 IAAddr,
115 IAPrefix,
116 OptionRequestOption,
117 Preference,
118 ElapsedTime,
119 ServerUnicast,
120 StatusCode,
121 RapidCommit,
122 DnsServers,
123 DomainList,
124 NtpServer,
125 Other(u16),
126}
127
128impl From<DhcpV6OptionCode> for u16 {
129 fn from(v: DhcpV6OptionCode) -> u16 {
130 match v {
131 DhcpV6OptionCode::ClientId => OPTION_CLIENTID,
132 DhcpV6OptionCode::ServerId => OPTION_SERVERID,
133 DhcpV6OptionCode::IANA => OPTION_IA_NA,
134 DhcpV6OptionCode::IATA => OPTION_IA_TA,
135 DhcpV6OptionCode::IAPD => OPTION_IA_PD,
136 DhcpV6OptionCode::IAAddr => OPTION_IAADDR,
137 DhcpV6OptionCode::IAPrefix => OPTION_IAPREFIX,
138 DhcpV6OptionCode::OptionRequestOption => OPTION_ORO,
139 DhcpV6OptionCode::Preference => OPTION_PREFERENCE,
140 DhcpV6OptionCode::ElapsedTime => OPTION_ELAPSED_TIME,
141 DhcpV6OptionCode::ServerUnicast => OPTION_UNICAST,
142 DhcpV6OptionCode::StatusCode => OPTION_STATUS_CODE,
143 DhcpV6OptionCode::RapidCommit => OPTION_RAPID_COMMIT,
144 DhcpV6OptionCode::DnsServers => OPTION_DNS_SERVERS,
145 DhcpV6OptionCode::DomainList => OPTION_DOMAIN_LIST,
146 DhcpV6OptionCode::NtpServer => OPTION_NTP_SERVER,
147 DhcpV6OptionCode::Other(d) => d,
148 }
149 }
150}
151
152impl From<u16> for DhcpV6OptionCode {
153 fn from(d: u16) -> Self {
154 match d {
155 OPTION_CLIENTID => Self::ClientId,
156 OPTION_SERVERID => Self::ServerId,
157 OPTION_IA_NA => Self::IANA,
158 OPTION_IA_TA => Self::IATA,
159 OPTION_IA_PD => Self::IAPD,
160 OPTION_IAADDR => Self::IAAddr,
161 OPTION_IAPREFIX => Self::IAPrefix,
162 OPTION_ORO => Self::OptionRequestOption,
163 OPTION_PREFERENCE => Self::Preference,
164 OPTION_ELAPSED_TIME => Self::ElapsedTime,
165 OPTION_UNICAST => Self::ServerUnicast,
166 OPTION_STATUS_CODE => Self::StatusCode,
167 OPTION_RAPID_COMMIT => Self::RapidCommit,
168 OPTION_DNS_SERVERS => Self::DnsServers,
169 OPTION_DOMAIN_LIST => Self::DomainList,
170 OPTION_NTP_SERVER => Self::NtpServer,
171 _ => Self::Other(d),
172 }
173 }
174}
175
176impl Ord for DhcpV6OptionCode {
177 fn cmp(&self, other: &Self) -> Ordering {
178 u16::from(*self).cmp(&u16::from(*other))
179 }
180}
181
182impl PartialOrd for DhcpV6OptionCode {
183 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
184 Some(self.cmp(other))
185 }
186}
187
188impl std::fmt::Display for DhcpV6OptionCode {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 match self {
191 Self::ClientId => write!(f, "OPTION_CLIENTID"),
192 Self::ServerId => write!(f, "OPTION_SERVERID"),
193 Self::IANA => write!(f, "OPTION_IA_NA"),
194 Self::IATA => write!(f, "OPTION_IA_TA"),
195 Self::IAPD => write!(f, "OPTION_IA_PD"),
196 Self::IAAddr => write!(f, "OPTION_IAADDR"),
197 Self::IAPrefix => write!(f, "OPTION_IAPREFIX"),
198 Self::OptionRequestOption => write!(f, "OPTION_ORO"),
199 Self::Preference => write!(f, "OPTION_PREFERENCE"),
200 Self::ElapsedTime => write!(f, "OPTION_ELAPSED_TIME"),
201 Self::ServerUnicast => write!(f, "OPTION_UNICAST"),
202 Self::StatusCode => write!(f, "OPTION_STATUS_CODE"),
203 Self::RapidCommit => write!(f, "OPTION_RAPID_COMMIT"),
204 Self::DnsServers => write!(f, "OPTION_DNS_SERVERS"),
205 Self::DomainList => write!(f, "OPTION_DOMAIN_LIST"),
206 Self::NtpServer => write!(f, "OPTION_NTP_SERVER"),
207 Self::Other(d) => write!(f, "Unknown({d})"),
208 }
209 }
210}
211
212#[derive(Debug, PartialEq, Eq, Clone)]
213#[non_exhaustive]
214pub enum DhcpV6Option {
215 ClientId(DhcpV6Duid),
216 ServerId(DhcpV6Duid),
217 IANA(DhcpV6OptionIaNa),
218 IATA(DhcpV6OptionIaTa),
219 IAPD(DhcpV6OptionIaPd),
220 OptionRequestOption(Vec<DhcpV6OptionCode>),
221 IAAddr(DhcpV6OptionIaAddr),
222 IAPrefix(DhcpV6OptionIaPrefix),
223 Preference(u8),
224 ElapsedTime(u16),
225 ServerUnicast(Ipv6Addr),
226 StatusCode(DhcpV6OptionStatus),
227 RapidCommit,
228 DnsServers(Vec<Ipv6Addr>),
230 DomainList(Vec<String>),
232 NtpServer(Vec<DhcpV6OptionNtpServer>),
234 Unknown(DhcpV6OptionUnknown),
235}
236
237impl Default for DhcpV6Option {
238 fn default() -> Self {
239 Self::Unknown(DhcpV6OptionUnknown::default())
240 }
241}
242
243impl Ord for DhcpV6Option {
244 fn cmp(&self, other: &Self) -> Ordering {
245 self.code().cmp(&other.code())
246 }
247}
248
249impl PartialOrd for DhcpV6Option {
250 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
251 Some(self.cmp(other))
252 }
253}
254
255impl DhcpV6Option {
256 pub fn code(&self) -> DhcpV6OptionCode {
257 match self {
258 DhcpV6Option::ClientId(_) => DhcpV6OptionCode::ClientId,
259 DhcpV6Option::ServerId(_) => DhcpV6OptionCode::ServerId,
260 DhcpV6Option::IANA(_) => DhcpV6OptionCode::IANA,
261 DhcpV6Option::IATA(_) => DhcpV6OptionCode::IATA,
262 DhcpV6Option::OptionRequestOption(_) => {
263 DhcpV6OptionCode::OptionRequestOption
264 }
265 DhcpV6Option::Preference(_) => DhcpV6OptionCode::Preference,
266 DhcpV6Option::ElapsedTime(_) => DhcpV6OptionCode::ElapsedTime,
267 DhcpV6Option::ServerUnicast(_) => DhcpV6OptionCode::ServerUnicast,
268 DhcpV6Option::StatusCode(_) => DhcpV6OptionCode::StatusCode,
269 DhcpV6Option::RapidCommit => DhcpV6OptionCode::RapidCommit,
270 DhcpV6Option::DnsServers(_) => DhcpV6OptionCode::DnsServers,
271 DhcpV6Option::DomainList(_) => DhcpV6OptionCode::DomainList,
272 DhcpV6Option::IAPD(_) => DhcpV6OptionCode::IAPD,
273 DhcpV6Option::NtpServer(_) => DhcpV6OptionCode::NtpServer,
274 DhcpV6Option::IAAddr(_) => DhcpV6OptionCode::IAAddr,
275 DhcpV6Option::IAPrefix(_) => DhcpV6OptionCode::IAPrefix,
276 DhcpV6Option::Unknown(u) => u.code(),
277 }
278 }
279
280 pub(crate) fn parse(buf: &mut Buffer) -> Result<DhcpV6Option, DhcpError> {
281 let code: DhcpV6OptionCode = buf
282 .peek_u16_be()
283 .context("Invalid DHCPv6 option code")?
284 .into();
285 let len: usize = buf
286 .peek_u16_be_offset(2)
287 .context("Invalid DHCPv6 option length")?
288 .into();
289 let opt_raw = buf.get_bytes(len + 4).context(format!(
290 "Invalid DHCPv6 option {code} with length {len}"
291 ))?;
292 let mut opt_buf = Buffer::new(opt_raw);
293
294 Ok(match code {
295 DhcpV6OptionCode::IAAddr => {
296 Self::IAAddr(DhcpV6OptionIaAddr::parse(&mut opt_buf)?)
297 }
298 DhcpV6OptionCode::IAPrefix => {
299 Self::IAPrefix(DhcpV6OptionIaPrefix::parse(&mut opt_buf)?)
300 }
301 DhcpV6OptionCode::ClientId => {
302 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
303 opt_buf
304 .get_u16_be()
305 .context("Invalid DHCPv6 option length")?;
306 Self::ClientId(DhcpV6Duid::parse(&mut opt_buf, len)?)
307 }
308 DhcpV6OptionCode::ServerId => {
309 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
310 opt_buf
311 .get_u16_be()
312 .context("Invalid DHCPv6 option length")?;
313 Self::ServerId(DhcpV6Duid::parse(&mut opt_buf, len)?)
314 }
315 DhcpV6OptionCode::IANA => {
316 Self::IANA(DhcpV6OptionIaNa::parse(&mut opt_buf)?)
317 }
318 DhcpV6OptionCode::IATA => {
319 Self::IATA(DhcpV6OptionIaTa::parse(&mut opt_buf)?)
320 }
321 DhcpV6OptionCode::IAPD => {
322 Self::IAPD(DhcpV6OptionIaPd::parse(&mut opt_buf)?)
323 }
324 DhcpV6OptionCode::OptionRequestOption => {
325 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
326 opt_buf
327 .get_u16_be()
328 .context("Invalid DHCPv6 option length")?;
329 let mut opts: Vec<DhcpV6OptionCode> = Vec::new();
330 for _ in 0..len / 2 {
331 opts.push(
332 opt_buf
333 .get_u16_be()
334 .context("Invalid DHCPv6 option OPTION_ORO")?
335 .into(),
336 );
337 }
338 Self::OptionRequestOption(opts)
339 }
340 DhcpV6OptionCode::Preference => Self::Preference({
341 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
342 opt_buf
343 .get_u16_be()
344 .context("Invalid DHCPv6 option length")?;
345 opt_buf
346 .get_u8()
347 .context("Invalid DHCPv6 option OPTION_PREFERENCE")?
348 }),
349 DhcpV6OptionCode::ElapsedTime => Self::ElapsedTime({
350 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
351 opt_buf
352 .get_u16_be()
353 .context("Invalid DHCPv6 option length")?;
354 opt_buf
355 .get_u16_be()
356 .context("Invalid DHCPv6 option OPTION_ELAPSED_TIME")?
357 }),
358 DhcpV6OptionCode::ServerUnicast => Self::ServerUnicast({
359 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
360 opt_buf
361 .get_u16_be()
362 .context("Invalid DHCPv6 option length")?;
363 opt_buf
364 .get_ipv6()
365 .context("Invalid DHCPv6 option OPTION_UNICAST")?
366 }),
367 DhcpV6OptionCode::StatusCode => {
368 Self::StatusCode(DhcpV6OptionStatus::parse(&mut opt_buf)?)
369 }
370 DhcpV6OptionCode::RapidCommit => {
371 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
372 opt_buf
373 .get_u16_be()
374 .context("Invalid DHCPv6 option length")?;
375 Self::RapidCommit
376 }
377 DhcpV6OptionCode::DnsServers => {
378 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
379 opt_buf
380 .get_u16_be()
381 .context("Invalid DHCPv6 option length")?;
382 let mut addrs = Vec::new();
383 for _ in 0..len / 16 {
384 addrs.push(
385 opt_buf.get_ipv6().context(
386 "Invalid DHCPv6 option OPTION_DNS_SERVERS",
387 )?,
388 );
389 }
390 Self::DnsServers(addrs)
391 }
392 DhcpV6OptionCode::DomainList => {
393 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
403 opt_buf
404 .get_u16_be()
405 .context("Invalid DHCPv6 option length")?;
406 let raw = opt_buf
407 .get_bytes(len)
408 .context("Invalid DHCPv6 option OPTION_DOMAIN_LIST")?;
409 let mut tmp_opt_buf = Buffer::new(raw);
410 let mut domains = Vec::new();
411 while !tmp_opt_buf.is_empty() {
412 let str_len = tmp_opt_buf.get_u8().context(
413 "Invalid DHCPv6 option OPTION_DOMAIN_LIST length",
414 )?;
415 domains.push(
416 tmp_opt_buf
417 .get_string_with_null(str_len.into())
418 .context(
419 "Invalid DHCPv6 option OPTION_DOMAIN_LIST \
420 domain",
421 )?,
422 );
423 }
424 Self::DomainList(domains)
425 }
426 DhcpV6OptionCode::NtpServer => {
427 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
429 opt_buf
430 .get_u16_be()
431 .context("Invalid DHCPv6 option length")?;
432 let raw = opt_buf
433 .get_bytes(len)
434 .context("Invalid DHCPv6 option OPTION_NTP_SERVER")?;
435 let mut tmp_opt_buf = Buffer::new(raw);
436 let mut srvs: Vec<DhcpV6OptionNtpServer> = Vec::new();
437 while !tmp_opt_buf.is_empty() {
438 srvs.push(DhcpV6OptionNtpServer::parse(&mut tmp_opt_buf)?);
439 }
440 Self::NtpServer(srvs)
441 }
442 DhcpV6OptionCode::Other(d) => Self::Unknown({
443 opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
444 opt_buf
445 .get_u16_be()
446 .context("Invalid DHCPv6 option length")?;
447 DhcpV6OptionUnknown {
448 code: d,
449 raw: opt_buf
450 .get_bytes(len)
451 .context(format!("Invalid DHCPv6 option {d}"))?
452 .to_vec(),
453 }
454 }),
455 })
456 }
457
458 pub(crate) fn emit(&self, buf: &mut BufferMut) {
459 match self {
460 Self::ClientId(id) | Self::ServerId(id) => {
461 let mut value_buf = BufferMut::new();
462 id.emit(&mut value_buf);
463 buf.write_u16_be(self.code().into());
464 buf.write_u16_be(value_buf.len() as u16);
465 buf.write_bytes(value_buf.data.as_slice());
466 }
467 Self::IAAddr(v) => v.emit(buf),
468 Self::IAPrefix(v) => v.emit(buf),
469 Self::IANA(v) => v.emit(buf),
470 Self::IATA(v) => v.emit(buf),
471 Self::IAPD(v) => v.emit(buf),
472 Self::OptionRequestOption(opts) => {
473 buf.write_u16_be(self.code().into());
474 buf.write_u16_be((opts.len() * 2) as u16);
475 for opt in opts {
476 buf.write_u16_be((*opt).into());
477 }
478 }
479 Self::Preference(d) => {
480 buf.write_u16_be(self.code().into());
481 buf.write_u16_be(1);
482 buf.write_u8(*d);
483 }
484 Self::ElapsedTime(d) => {
485 buf.write_u16_be(self.code().into());
486 buf.write_u16_be(2);
487 buf.write_u16_be(*d);
488 }
489 Self::ServerUnicast(i) => {
490 buf.write_u16_be(self.code().into());
491 buf.write_u16_be(16);
492 buf.write_ipv6(*i);
493 }
494 Self::StatusCode(v) => {
495 v.emit(buf);
496 }
497 Self::RapidCommit => {
498 buf.write_u16_be(self.code().into());
499 buf.write_u16_be(0);
500 }
501 Self::DnsServers(addrs) => {
502 buf.write_u16_be(self.code().into());
503 buf.write_u16_be((addrs.len() * 16) as u16);
504 for addr in addrs {
505 buf.write_ipv6(*addr);
506 }
507 }
508 Self::DomainList(domains) => {
509 let mut value_buf = BufferMut::new();
510 for domain in domains {
511 value_buf.write_u8((domain.len() + 1) as u8);
512 value_buf.write_string_with_null(domain, domain.len() + 1);
513 }
514 buf.write_u16_be(self.code().into());
515 buf.write_u16_be(value_buf.len() as u16);
516 buf.write_bytes(value_buf.data.as_slice());
517 }
518 Self::NtpServer(srvs) => {
519 let mut value_buf = BufferMut::new();
520 for srv in srvs {
521 srv.emit(&mut value_buf);
522 }
523 buf.write_u16_be(self.code().into());
524 buf.write_u16_be(value_buf.len() as u16);
525 buf.write_bytes(value_buf.data.as_slice());
526 }
527 Self::Unknown(v) => {
528 buf.write_u16_be(self.code().into());
529 buf.write_u16_be(v.raw.len() as u16);
530 buf.write_bytes(v.raw.as_slice());
531 }
532 }
533 }
534}
535
536#[derive(Debug, PartialEq, Eq, Clone, Default)]
537pub struct DhcpV6OptionUnknown {
538 pub code: u16,
539 pub raw: Vec<u8>,
540}
541
542impl DhcpV6OptionUnknown {
543 pub fn code(&self) -> DhcpV6OptionCode {
544 self.code.into()
545 }
546}
547
548#[derive(Debug, PartialEq, Eq, Clone)]
552#[non_exhaustive]
553pub enum DhcpV6OptionNtpServer {
554 ServerAddr(Ipv6Addr),
555 MulticastAddr(Ipv6Addr),
556 ServerFqdn(String),
557 Other((u16, Vec<u8>)),
558}
559
560const NTP_SUBOPTION_SRV_ADDR: u16 = 1;
561const NTP_SUBOPTION_MC_ADDR: u16 = 2;
562const NTP_SUBOPTION_SRV_FQDN: u16 = 3;
563
564impl DhcpV6OptionNtpServer {
565 pub(crate) fn parse(buf: &mut Buffer) -> Result<Self, DhcpError> {
566 let subopt_type = buf
567 .get_u16_be()
568 .context("Invalid OPTION_NTP_SERVER suboption")?;
569 let subopt_len = buf
570 .get_u16_be()
571 .context("Invalid OPTION_NTP_SERVER suboption-len")?;
572 Ok(match subopt_type {
573 NTP_SUBOPTION_SRV_ADDR => {
574 Self::ServerAddr(buf.get_ipv6().context(
575 "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_ADDR",
576 )?)
577 }
578 NTP_SUBOPTION_MC_ADDR => {
579 Self::MulticastAddr(buf.get_ipv6().context(
580 "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_MC_ADDR",
581 )?)
582 }
583 NTP_SUBOPTION_SRV_FQDN => Self::ServerFqdn({
584 let mut lables = Vec::new();
585 let raw = buf.get_bytes(subopt_len.into()).context(
586 "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_FQDN",
587 )?;
588 let mut fqdn_buf = Buffer::new(raw);
589 while !fqdn_buf.is_empty() {
590 let lable_len = fqdn_buf.get_u8().context(
591 "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_FQDN",
592 )?;
593 if lable_len == 0 {
594 break;
595 }
596 let lable_raw =
597 fqdn_buf.get_bytes(lable_len as usize).context(
598 "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_FQDN",
599 )?;
600 match std::str::from_utf8(lable_raw) {
601 Ok(l) => lables.push(l.to_string()),
602 Err(e) => {
603 return Err(DhcpError::new(
604 ErrorKind::InvalidDhcpMessage,
605 format!(
606 "Invalid OPTION_NTP_SERVER \
607 NTP_SUBOPTION_SRV_FQDN: {e}"
608 ),
609 ));
610 }
611 }
612 }
613 lables.join(".").to_string()
614 }),
615 _ => Self::Other((
616 subopt_type,
617 buf.get_bytes(subopt_len.into())
618 .context(format!(
619 "Invalid OPTION_NTP_SERVER {}",
620 subopt_type
621 ))?
622 .to_vec(),
623 )),
624 })
625 }
626
627 pub(crate) fn emit(&self, buf: &mut BufferMut) {
628 match self {
629 Self::ServerAddr(ip) => {
630 buf.write_u16_be(NTP_SUBOPTION_SRV_ADDR);
631 buf.write_u16_be(16);
632 buf.write_ipv6(*ip);
633 }
634 Self::MulticastAddr(ip) => {
635 buf.write_u16_be(NTP_SUBOPTION_MC_ADDR);
636 buf.write_u16_be(16);
637 buf.write_ipv6(*ip);
638 }
639 Self::ServerFqdn(name) => {
640 buf.write_u16_be(NTP_SUBOPTION_SRV_FQDN);
641 buf.write_u16_be((name.len() + 1) as u16);
642 buf.write_string_with_null(name, name.len() + 1);
643 }
644 Self::Other((subopt_type, v)) => {
645 buf.write_u16_be(*subopt_type);
646 buf.write_u16_be(v.len() as u16);
647 buf.write_bytes(v.as_slice());
648 }
649 }
650 }
651}