1use bitflags::bitflags;
4use byteorder::{ByteOrder, NetworkEndian};
5use core::iter;
6use heapless::Vec;
7
8use super::{Error, Result};
9use crate::wire::arp::Hardware;
10use crate::wire::{EthernetAddress, Ipv4Address, Ipv4AddressExt};
11
12pub const SERVER_PORT: u16 = 67;
13pub const CLIENT_PORT: u16 = 68;
14pub const MAX_DNS_SERVER_COUNT: usize = 3;
15
16const DHCP_MAGIC_NUMBER: u32 = 0x63825363;
17
18enum_with_unknown! {
19 pub enum OpCode(u8) {
21 Request = 1,
22 Reply = 2,
23 }
24}
25
26enum_with_unknown! {
27 pub enum MessageType(u8) {
29 Discover = 1,
30 Offer = 2,
31 Request = 3,
32 Decline = 4,
33 Ack = 5,
34 Nak = 6,
35 Release = 7,
36 Inform = 8,
37 }
38}
39
40bitflags! {
41 pub struct Flags: u16 {
42 const BROADCAST = 0b1000_0000_0000_0000;
43 }
44}
45
46impl MessageType {
47 const fn opcode(&self) -> OpCode {
48 match *self {
49 MessageType::Discover
50 | MessageType::Inform
51 | MessageType::Request
52 | MessageType::Decline
53 | MessageType::Release => OpCode::Request,
54 MessageType::Offer | MessageType::Ack | MessageType::Nak => OpCode::Reply,
55 MessageType::Unknown(_) => OpCode::Unknown(0),
56 }
57 }
58}
59
60#[derive(Debug)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63pub struct DhcpOptionWriter<'a> {
64 buffer: &'a mut [u8],
66}
67
68impl<'a> DhcpOptionWriter<'a> {
69 pub fn new(buffer: &'a mut [u8]) -> Self {
70 Self { buffer }
71 }
72
73 pub fn emit(&mut self, option: DhcpOption<'_>) -> Result<()> {
75 if option.data.len() > u8::MAX as _ {
76 return Err(Error);
77 }
78
79 let total_len = 2 + option.data.len();
80 if self.buffer.len() < total_len {
81 return Err(Error);
82 }
83
84 let (buf, rest) = core::mem::take(&mut self.buffer).split_at_mut(total_len);
85 self.buffer = rest;
86
87 buf[0] = option.kind;
88 buf[1] = option.data.len() as _;
89 buf[2..].copy_from_slice(option.data);
90
91 Ok(())
92 }
93
94 pub fn end(&mut self) -> Result<()> {
95 if self.buffer.is_empty() {
96 return Err(Error);
97 }
98
99 self.buffer[0] = field::OPT_END;
100 self.buffer = &mut [];
101 Ok(())
102 }
103}
104
105#[derive(Debug, PartialEq, Eq, Clone, Copy)]
107#[cfg_attr(feature = "defmt", derive(defmt::Format))]
108pub struct DhcpOption<'a> {
109 pub kind: u8,
110 pub data: &'a [u8],
111}
112
113#[derive(Debug, PartialEq, Eq, Copy, Clone)]
115#[cfg_attr(feature = "defmt", derive(defmt::Format))]
116pub struct Packet<T: AsRef<[u8]>> {
117 buffer: T,
118}
119
120pub(crate) mod field {
121 #![allow(non_snake_case)]
122 #![allow(unused)]
123
124 use crate::wire::field::*;
125
126 pub const OP: usize = 0;
127 pub const HTYPE: usize = 1;
128 pub const HLEN: usize = 2;
129 pub const HOPS: usize = 3;
130 pub const XID: Field = 4..8;
131 pub const SECS: Field = 8..10;
132 pub const FLAGS: Field = 10..12;
133 pub const CIADDR: Field = 12..16;
134 pub const YIADDR: Field = 16..20;
135 pub const SIADDR: Field = 20..24;
136 pub const GIADDR: Field = 24..28;
137 pub const CHADDR: Field = 28..34;
138 pub const SNAME: Field = 34..108;
139 pub const FILE: Field = 108..236;
140 pub const MAGIC_NUMBER: Field = 236..240;
141 pub const OPTIONS: Rest = 240..;
142
143 pub const OPT_END: u8 = 255;
145 pub const OPT_PAD: u8 = 0;
146 pub const OPT_SUBNET_MASK: u8 = 1;
147 pub const OPT_TIME_OFFSET: u8 = 2;
148 pub const OPT_ROUTER: u8 = 3;
149 pub const OPT_TIME_SERVER: u8 = 4;
150 pub const OPT_NAME_SERVER: u8 = 5;
151 pub const OPT_DOMAIN_NAME_SERVER: u8 = 6;
152 pub const OPT_LOG_SERVER: u8 = 7;
153 pub const OPT_COOKIE_SERVER: u8 = 8;
154 pub const OPT_LPR_SERVER: u8 = 9;
155 pub const OPT_IMPRESS_SERVER: u8 = 10;
156 pub const OPT_RESOURCE_LOCATION_SERVER: u8 = 11;
157 pub const OPT_HOST_NAME: u8 = 12;
158 pub const OPT_BOOT_FILE_SIZE: u8 = 13;
159 pub const OPT_MERIT_DUMP: u8 = 14;
160 pub const OPT_DOMAIN_NAME: u8 = 15;
161 pub const OPT_SWAP_SERVER: u8 = 16;
162 pub const OPT_ROOT_PATH: u8 = 17;
163 pub const OPT_EXTENSIONS_PATH: u8 = 18;
164
165 pub const OPT_IP_FORWARDING: u8 = 19;
167 pub const OPT_NON_LOCAL_SOURCE_ROUTING: u8 = 20;
168 pub const OPT_POLICY_FILTER: u8 = 21;
169 pub const OPT_MAX_DATAGRAM_REASSEMBLY_SIZE: u8 = 22;
170 pub const OPT_DEFAULT_TTL: u8 = 23;
171 pub const OPT_PATH_MTU_AGING_TIMEOUT: u8 = 24;
172 pub const OPT_PATH_MTU_PLATEAU_TABLE: u8 = 25;
173
174 pub const OPT_INTERFACE_MTU: u8 = 26;
176 pub const OPT_ALL_SUBNETS_ARE_LOCAL: u8 = 27;
177 pub const OPT_BROADCAST_ADDRESS: u8 = 28;
178 pub const OPT_PERFORM_MASK_DISCOVERY: u8 = 29;
179 pub const OPT_MASK_SUPPLIER: u8 = 30;
180 pub const OPT_PERFORM_ROUTER_DISCOVERY: u8 = 31;
181 pub const OPT_ROUTER_SOLICITATION_ADDRESS: u8 = 32;
182 pub const OPT_STATIC_ROUTE: u8 = 33;
183
184 pub const OPT_TRAILER_ENCAPSULATION: u8 = 34;
186 pub const OPT_ARP_CACHE_TIMEOUT: u8 = 35;
187 pub const OPT_ETHERNET_ENCAPSULATION: u8 = 36;
188
189 pub const OPT_TCP_DEFAULT_TTL: u8 = 37;
191 pub const OPT_TCP_KEEPALIVE_INTERVAL: u8 = 38;
192 pub const OPT_TCP_KEEPALIVE_GARBAGE: u8 = 39;
193
194 pub const OPT_NIS_DOMAIN: u8 = 40;
196 pub const OPT_NIS_SERVERS: u8 = 41;
197 pub const OPT_NTP_SERVERS: u8 = 42;
198 pub const OPT_VENDOR_SPECIFIC_INFO: u8 = 43;
199 pub const OPT_NETBIOS_NAME_SERVER: u8 = 44;
200 pub const OPT_NETBIOS_DISTRIBUTION_SERVER: u8 = 45;
201 pub const OPT_NETBIOS_NODE_TYPE: u8 = 46;
202 pub const OPT_NETBIOS_SCOPE: u8 = 47;
203 pub const OPT_X_WINDOW_FONT_SERVER: u8 = 48;
204 pub const OPT_X_WINDOW_DISPLAY_MANAGER: u8 = 49;
205 pub const OPT_NIS_PLUS_DOMAIN: u8 = 64;
206 pub const OPT_NIS_PLUS_SERVERS: u8 = 65;
207 pub const OPT_MOBILE_IP_HOME_AGENT: u8 = 68;
208 pub const OPT_SMTP_SERVER: u8 = 69;
209 pub const OPT_POP3_SERVER: u8 = 70;
210 pub const OPT_NNTP_SERVER: u8 = 71;
211 pub const OPT_WWW_SERVER: u8 = 72;
212 pub const OPT_FINGER_SERVER: u8 = 73;
213 pub const OPT_IRC_SERVER: u8 = 74;
214 pub const OPT_STREETTALK_SERVER: u8 = 75;
215 pub const OPT_STDA_SERVER: u8 = 76;
216
217 pub const OPT_REQUESTED_IP: u8 = 50;
219 pub const OPT_IP_LEASE_TIME: u8 = 51;
220 pub const OPT_OPTION_OVERLOAD: u8 = 52;
221 pub const OPT_TFTP_SERVER_NAME: u8 = 66;
222 pub const OPT_BOOTFILE_NAME: u8 = 67;
223 pub const OPT_DHCP_MESSAGE_TYPE: u8 = 53;
224 pub const OPT_SERVER_IDENTIFIER: u8 = 54;
225 pub const OPT_PARAMETER_REQUEST_LIST: u8 = 55;
226 pub const OPT_MESSAGE: u8 = 56;
227 pub const OPT_MAX_DHCP_MESSAGE_SIZE: u8 = 57;
228 pub const OPT_RENEWAL_TIME_VALUE: u8 = 58;
229 pub const OPT_REBINDING_TIME_VALUE: u8 = 59;
230 pub const OPT_VENDOR_CLASS_ID: u8 = 60;
231 pub const OPT_CLIENT_ID: u8 = 61;
232}
233
234impl<T: AsRef<[u8]>> Packet<T> {
235 pub const fn new_unchecked(buffer: T) -> Packet<T> {
237 Packet { buffer }
238 }
239
240 pub fn new_checked(buffer: T) -> Result<Packet<T>> {
245 let packet = Self::new_unchecked(buffer);
246 packet.check_len()?;
247 Ok(packet)
248 }
249
250 pub fn check_len(&self) -> Result<()> {
255 let len = self.buffer.as_ref().len();
256 if len < field::MAGIC_NUMBER.end {
257 Err(Error)
258 } else {
259 Ok(())
260 }
261 }
262
263 pub fn into_inner(self) -> T {
265 self.buffer
266 }
267
268 pub fn opcode(&self) -> OpCode {
270 let data = self.buffer.as_ref();
271 OpCode::from(data[field::OP])
272 }
273
274 pub fn hardware_type(&self) -> Hardware {
276 let data = self.buffer.as_ref();
277 Hardware::from(u16::from(data[field::HTYPE]))
278 }
279
280 pub fn hardware_len(&self) -> u8 {
282 self.buffer.as_ref()[field::HLEN]
283 }
284
285 pub fn transaction_id(&self) -> u32 {
291 let field = &self.buffer.as_ref()[field::XID];
292 NetworkEndian::read_u32(field)
293 }
294
295 pub fn client_hardware_address(&self) -> EthernetAddress {
300 let field = &self.buffer.as_ref()[field::CHADDR];
301 EthernetAddress::from_bytes(field)
302 }
303
304 pub fn hops(&self) -> u8 {
308 self.buffer.as_ref()[field::HOPS]
309 }
310
311 pub fn secs(&self) -> u16 {
316 let field = &self.buffer.as_ref()[field::SECS];
317 NetworkEndian::read_u16(field)
318 }
319
320 pub fn magic_number(&self) -> u32 {
324 let field = &self.buffer.as_ref()[field::MAGIC_NUMBER];
325 NetworkEndian::read_u32(field)
326 }
327
328 pub fn client_ip(&self) -> Ipv4Address {
334 let field = &self.buffer.as_ref()[field::CIADDR];
335 Ipv4Address::from_bytes(field)
336 }
337
338 pub fn your_ip(&self) -> Ipv4Address {
340 let field = &self.buffer.as_ref()[field::YIADDR];
341 Ipv4Address::from_bytes(field)
342 }
343
344 pub fn server_ip(&self) -> Ipv4Address {
346 let field = &self.buffer.as_ref()[field::SIADDR];
347 Ipv4Address::from_bytes(field)
348 }
349
350 pub fn relay_agent_ip(&self) -> Ipv4Address {
352 let field = &self.buffer.as_ref()[field::GIADDR];
353 Ipv4Address::from_bytes(field)
354 }
355
356 pub fn flags(&self) -> Flags {
357 let field = &self.buffer.as_ref()[field::FLAGS];
358 Flags::from_bits_truncate(NetworkEndian::read_u16(field))
359 }
360
361 #[inline]
363 pub fn options(&self) -> impl Iterator<Item = DhcpOption<'_>> + '_ {
364 let mut buf = &self.buffer.as_ref()[field::OPTIONS];
365 iter::from_fn(move || {
366 loop {
367 match buf.first().copied() {
368 None => return None,
370 Some(field::OPT_END) => return None,
371
372 Some(field::OPT_PAD) => buf = &buf[1..],
374 Some(kind) => {
375 if buf.len() < 2 {
376 return None;
377 }
378
379 let len = buf[1] as usize;
380
381 if buf.len() < 2 + len {
382 return None;
383 }
384
385 let opt = DhcpOption {
386 kind,
387 data: &buf[2..2 + len],
388 };
389
390 buf = &buf[2 + len..];
391 return Some(opt);
392 }
393 }
394 }
395 })
396 }
397
398 pub fn get_sname(&self) -> Result<&str> {
399 let data = &self.buffer.as_ref()[field::SNAME];
400 let len = data.iter().position(|&x| x == 0).ok_or(Error)?;
401 if len == 0 {
402 return Err(Error);
403 }
404
405 let data = core::str::from_utf8(&data[..len]).map_err(|_| Error)?;
406 Ok(data)
407 }
408
409 pub fn get_boot_file(&self) -> Result<&str> {
410 let data = &self.buffer.as_ref()[field::FILE];
411 let len = data.iter().position(|&x| x == 0).ok_or(Error)?;
412 if len == 0 {
413 return Err(Error);
414 }
415 let data = core::str::from_utf8(&data[..len]).map_err(|_| Error)?;
416 Ok(data)
417 }
418}
419
420impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
421 pub fn set_sname_and_boot_file_to_zero(&mut self) {
427 let data = self.buffer.as_mut();
428 for byte in &mut data[field::SNAME] {
429 *byte = 0;
430 }
431 for byte in &mut data[field::FILE] {
432 *byte = 0;
433 }
434 }
435
436 pub fn set_opcode(&mut self, value: OpCode) {
438 let data = self.buffer.as_mut();
439 data[field::OP] = value.into();
440 }
441
442 pub fn set_hardware_type(&mut self, value: Hardware) {
444 let data = self.buffer.as_mut();
445 let number: u16 = value.into();
446 assert!(number <= u16::from(u8::MAX)); data[field::HTYPE] = number as u8;
448 }
449
450 pub fn set_hardware_len(&mut self, value: u8) {
454 self.buffer.as_mut()[field::HLEN] = value;
455 }
456
457 pub fn set_transaction_id(&mut self, value: u32) {
463 let field = &mut self.buffer.as_mut()[field::XID];
464 NetworkEndian::write_u32(field, value)
465 }
466
467 pub fn set_client_hardware_address(&mut self, value: EthernetAddress) {
471 let field = &mut self.buffer.as_mut()[field::CHADDR];
472 field.copy_from_slice(value.as_bytes());
473 }
474
475 pub fn set_hops(&mut self, value: u8) {
479 self.buffer.as_mut()[field::HOPS] = value;
480 }
481
482 pub fn set_secs(&mut self, value: u16) {
487 let field = &mut self.buffer.as_mut()[field::SECS];
488 NetworkEndian::write_u16(field, value);
489 }
490
491 pub fn set_magic_number(&mut self, value: u32) {
495 let field = &mut self.buffer.as_mut()[field::MAGIC_NUMBER];
496 NetworkEndian::write_u32(field, value);
497 }
498
499 pub fn set_client_ip(&mut self, value: Ipv4Address) {
505 let field = &mut self.buffer.as_mut()[field::CIADDR];
506 field.copy_from_slice(&value.octets());
507 }
508
509 pub fn set_your_ip(&mut self, value: Ipv4Address) {
511 let field = &mut self.buffer.as_mut()[field::YIADDR];
512 field.copy_from_slice(&value.octets());
513 }
514
515 pub fn set_server_ip(&mut self, value: Ipv4Address) {
517 let field = &mut self.buffer.as_mut()[field::SIADDR];
518 field.copy_from_slice(&value.octets());
519 }
520
521 pub fn set_relay_agent_ip(&mut self, value: Ipv4Address) {
523 let field = &mut self.buffer.as_mut()[field::GIADDR];
524 field.copy_from_slice(&value.octets());
525 }
526
527 pub fn set_flags(&mut self, val: Flags) {
529 let field = &mut self.buffer.as_mut()[field::FLAGS];
530 NetworkEndian::write_u16(field, val.bits());
531 }
532}
533
534impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
535 #[inline]
537 pub fn options_mut(&mut self) -> DhcpOptionWriter<'_> {
538 DhcpOptionWriter::new(&mut self.buffer.as_mut()[field::OPTIONS])
539 }
540}
541
542#[derive(Debug, PartialEq, Eq, Clone)]
587#[cfg_attr(feature = "defmt", derive(defmt::Format))]
588pub struct Repr<'a> {
589 pub message_type: MessageType,
592 pub transaction_id: u32,
596 pub secs: u16,
601 pub client_hardware_address: EthernetAddress,
604 pub client_ip: Ipv4Address,
607 pub your_ip: Ipv4Address,
609 pub server_ip: Ipv4Address,
612 pub router: Option<Ipv4Address>,
614 pub subnet_mask: Option<Ipv4Address>,
616 pub relay_agent_ip: Ipv4Address,
626 pub broadcast: bool,
629 pub requested_ip: Option<Ipv4Address>,
632 pub client_identifier: Option<EthernetAddress>,
642 pub server_identifier: Option<Ipv4Address>,
645 pub parameter_request_list: Option<&'a [u8]>,
648 pub dns_servers: Option<Vec<Ipv4Address, MAX_DNS_SERVER_COUNT>>,
650 pub max_size: Option<u16>,
652 pub lease_duration: Option<u32>,
654 pub renew_duration: Option<u32>,
656 pub rebind_duration: Option<u32>,
658 pub additional_options: &'a [DhcpOption<'a>],
662}
663
664impl<'a> Repr<'a> {
665 pub fn buffer_len(&self) -> usize {
667 let mut len = field::OPTIONS.start;
668 len += 3 + 1;
670 if self.requested_ip.is_some() {
671 len += 6;
672 }
673 if self.client_identifier.is_some() {
674 len += 9;
675 }
676 if self.server_identifier.is_some() {
677 len += 6;
678 }
679 if self.max_size.is_some() {
680 len += 4;
681 }
682 if self.router.is_some() {
683 len += 6;
684 }
685 if self.subnet_mask.is_some() {
686 len += 6;
687 }
688 if self.lease_duration.is_some() {
689 len += 6;
690 }
691 if let Some(dns_servers) = &self.dns_servers {
692 len += 2;
693 len += dns_servers.iter().count() * core::mem::size_of::<u32>();
694 }
695 if let Some(list) = self.parameter_request_list {
696 len += list.len() + 2;
697 }
698 for opt in self.additional_options {
699 len += 2 + opt.data.len()
700 }
701
702 len
703 }
704
705 pub fn parse<T>(packet: &'a Packet<&'a T>) -> Result<Self>
707 where
708 T: AsRef<[u8]> + ?Sized,
709 {
710 packet.check_len()?;
711 let transaction_id = packet.transaction_id();
712 let client_hardware_address = packet.client_hardware_address();
713 let client_ip = packet.client_ip();
714 let your_ip = packet.your_ip();
715 let server_ip = packet.server_ip();
716 let relay_agent_ip = packet.relay_agent_ip();
717 let secs = packet.secs();
718
719 match packet.hardware_type() {
721 Hardware::Ethernet => {
722 if packet.hardware_len() != 6 {
723 return Err(Error);
724 }
725 }
726 Hardware::Unknown(_) => return Err(Error), }
728
729 if packet.magic_number() != DHCP_MAGIC_NUMBER {
730 return Err(Error);
731 }
732
733 let mut message_type = Err(Error);
734 let mut requested_ip = None;
735 let mut client_identifier = None;
736 let mut server_identifier = None;
737 let mut router = None;
738 let mut subnet_mask = None;
739 let mut parameter_request_list = None;
740 let mut dns_servers = None;
741 let mut max_size = None;
742 let mut lease_duration = None;
743 let mut renew_duration = None;
744 let mut rebind_duration = None;
745
746 for option in packet.options() {
747 let data = option.data;
748 match (option.kind, data.len()) {
749 (field::OPT_DHCP_MESSAGE_TYPE, 1) => {
750 let value = MessageType::from(data[0]);
751 if value.opcode() == packet.opcode() {
752 message_type = Ok(value);
753 }
754 }
755 (field::OPT_REQUESTED_IP, 4) => {
756 requested_ip = Some(Ipv4Address::from_bytes(data));
757 }
758 (field::OPT_CLIENT_ID, 7) => {
759 let hardware_type = Hardware::from(u16::from(data[0]));
760 if hardware_type != Hardware::Ethernet {
761 return Err(Error);
762 }
763 client_identifier = Some(EthernetAddress::from_bytes(&data[1..]));
764 }
765 (field::OPT_SERVER_IDENTIFIER, 4) => {
766 server_identifier = Some(Ipv4Address::from_bytes(data));
767 }
768 (field::OPT_ROUTER, 4) => {
769 router = Some(Ipv4Address::from_bytes(data));
770 }
771 (field::OPT_SUBNET_MASK, 4) => {
772 subnet_mask = Some(Ipv4Address::from_bytes(data));
773 }
774 (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
775 max_size = Some(u16::from_be_bytes([data[0], data[1]]));
776 }
777 (field::OPT_RENEWAL_TIME_VALUE, 4) => {
778 renew_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
779 }
780 (field::OPT_REBINDING_TIME_VALUE, 4) => {
781 rebind_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
782 }
783 (field::OPT_IP_LEASE_TIME, 4) => {
784 lease_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
785 }
786 (field::OPT_PARAMETER_REQUEST_LIST, _) => {
787 parameter_request_list = Some(data);
788 }
789 (field::OPT_DOMAIN_NAME_SERVER, _) => {
790 let mut servers = Vec::new();
791 const IP_ADDR_BYTE_LEN: usize = 4;
792 let mut addrs = data.chunks_exact(IP_ADDR_BYTE_LEN);
793 for chunk in &mut addrs {
794 servers.push(Ipv4Address::from_bytes(chunk)).ok();
798 }
799 dns_servers = Some(servers);
800
801 if !addrs.remainder().is_empty() {
802 net_trace!("DHCP domain name servers contained invalid address");
803 }
804 }
805 _ => {}
806 }
807 }
808
809 let broadcast = packet.flags().contains(Flags::BROADCAST);
810
811 Ok(Repr {
812 secs,
813 transaction_id,
814 client_hardware_address,
815 client_ip,
816 your_ip,
817 server_ip,
818 relay_agent_ip,
819 broadcast,
820 requested_ip,
821 server_identifier,
822 router,
823 subnet_mask,
824 client_identifier,
825 parameter_request_list,
826 dns_servers,
827 max_size,
828 lease_duration,
829 renew_duration,
830 rebind_duration,
831 message_type: message_type?,
832 additional_options: &[],
833 })
834 }
835
836 pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
839 where
840 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
841 {
842 packet.set_sname_and_boot_file_to_zero();
843 packet.set_opcode(self.message_type.opcode());
844 packet.set_hardware_type(Hardware::Ethernet);
845 packet.set_hardware_len(6);
846 packet.set_transaction_id(self.transaction_id);
847 packet.set_client_hardware_address(self.client_hardware_address);
848 packet.set_hops(0);
849 packet.set_secs(self.secs);
850 packet.set_magic_number(0x63825363);
851 packet.set_client_ip(self.client_ip);
852 packet.set_your_ip(self.your_ip);
853 packet.set_server_ip(self.server_ip);
854 packet.set_relay_agent_ip(self.relay_agent_ip);
855
856 let mut flags = Flags::empty();
857 if self.broadcast {
858 flags |= Flags::BROADCAST;
859 }
860 packet.set_flags(flags);
861
862 {
863 let mut options = packet.options_mut();
864
865 options.emit(DhcpOption {
866 kind: field::OPT_DHCP_MESSAGE_TYPE,
867 data: &[self.message_type.into()],
868 })?;
869
870 if let Some(val) = &self.client_identifier {
871 let mut data = [0; 7];
872 data[0] = u16::from(Hardware::Ethernet) as u8;
873 data[1..].copy_from_slice(val.as_bytes());
874
875 options.emit(DhcpOption {
876 kind: field::OPT_CLIENT_ID,
877 data: &data,
878 })?;
879 }
880
881 if let Some(val) = &self.server_identifier {
882 options.emit(DhcpOption {
883 kind: field::OPT_SERVER_IDENTIFIER,
884 data: &val.octets(),
885 })?;
886 }
887
888 if let Some(val) = &self.router {
889 options.emit(DhcpOption {
890 kind: field::OPT_ROUTER,
891 data: &val.octets(),
892 })?;
893 }
894 if let Some(val) = &self.subnet_mask {
895 options.emit(DhcpOption {
896 kind: field::OPT_SUBNET_MASK,
897 data: &val.octets(),
898 })?;
899 }
900 if let Some(val) = &self.requested_ip {
901 options.emit(DhcpOption {
902 kind: field::OPT_REQUESTED_IP,
903 data: &val.octets(),
904 })?;
905 }
906 if let Some(val) = &self.max_size {
907 options.emit(DhcpOption {
908 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
909 data: &val.to_be_bytes(),
910 })?;
911 }
912 if let Some(val) = &self.lease_duration {
913 options.emit(DhcpOption {
914 kind: field::OPT_IP_LEASE_TIME,
915 data: &val.to_be_bytes(),
916 })?;
917 }
918 if let Some(val) = &self.parameter_request_list {
919 options.emit(DhcpOption {
920 kind: field::OPT_PARAMETER_REQUEST_LIST,
921 data: val,
922 })?;
923 }
924
925 if let Some(dns_servers) = &self.dns_servers {
926 const IP_SIZE: usize = core::mem::size_of::<u32>();
927 let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE];
928
929 let data_len = dns_servers
930 .iter()
931 .enumerate()
932 .inspect(|(i, ip)| {
933 servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(&ip.octets());
934 })
935 .count()
936 * IP_SIZE;
937 options.emit(DhcpOption {
938 kind: field::OPT_DOMAIN_NAME_SERVER,
939 data: &servers[..data_len],
940 })?;
941 }
942
943 for option in self.additional_options {
944 options.emit(*option)?;
945 }
946
947 options.end()?;
948 }
949
950 Ok(())
951 }
952}
953
954#[cfg(test)]
955mod test {
956 use super::*;
957 use crate::wire::Ipv4Address;
958
959 const MAGIC_COOKIE: u32 = 0x63825363;
960
961 static DISCOVER_BYTES: &[u8] = &[
962 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
963 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
964 0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
965 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
966 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
967 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
968 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
969 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
970 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
971 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
972 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
973 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
974 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
975 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
976 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
977 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
978 0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00,
979 0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00,
980 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
981 ];
982
983 static ACK_DNS_SERVER_BYTES: &[u8] = &[
984 0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06,
985 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17,
986 0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
987 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
988 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
989 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
990 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
991 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
992 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
993 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
994 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
995 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
996 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
997 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
998 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
999 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
1000 0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00,
1001 0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68,
1002 0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00,
1003 0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a,
1004 0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03,
1005 0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08,
1006 0xff,
1007 ];
1008
1009 static ACK_LEASE_TIME_BYTES: &[u8] = &[
1010 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1011 0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91,
1012 0x62, 0xd2, 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1013 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1014 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1015 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1016 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1017 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1018 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1019 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1020 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1021 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1022 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1023 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1024 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1025 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
1026 0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56,
1027 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00,
1028 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1029 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1030 ];
1031
1032 const IP_NULL: Ipv4Address = Ipv4Address::new(0, 0, 0, 0);
1033 const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
1034 const DHCP_SIZE: u16 = 1500;
1035
1036 #[test]
1037 fn test_deconstruct_discover() {
1038 let packet = Packet::new_unchecked(DISCOVER_BYTES);
1039 assert_eq!(packet.magic_number(), MAGIC_COOKIE);
1040 assert_eq!(packet.opcode(), OpCode::Request);
1041 assert_eq!(packet.hardware_type(), Hardware::Ethernet);
1042 assert_eq!(packet.hardware_len(), 6);
1043 assert_eq!(packet.hops(), 0);
1044 assert_eq!(packet.transaction_id(), 0x3d1d);
1045 assert_eq!(packet.secs(), 0);
1046 assert_eq!(packet.client_ip(), IP_NULL);
1047 assert_eq!(packet.your_ip(), IP_NULL);
1048 assert_eq!(packet.server_ip(), IP_NULL);
1049 assert_eq!(packet.relay_agent_ip(), IP_NULL);
1050 assert_eq!(packet.client_hardware_address(), CLIENT_MAC);
1051
1052 let mut options = packet.options();
1053 assert_eq!(
1054 options.next(),
1055 Some(DhcpOption {
1056 kind: field::OPT_DHCP_MESSAGE_TYPE,
1057 data: &[0x01]
1058 })
1059 );
1060 assert_eq!(
1061 options.next(),
1062 Some(DhcpOption {
1063 kind: field::OPT_CLIENT_ID,
1064 data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1065 })
1066 );
1067 assert_eq!(
1068 options.next(),
1069 Some(DhcpOption {
1070 kind: field::OPT_REQUESTED_IP,
1071 data: &[0x00, 0x00, 0x00, 0x00],
1072 })
1073 );
1074 assert_eq!(
1075 options.next(),
1076 Some(DhcpOption {
1077 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1078 data: &DHCP_SIZE.to_be_bytes(),
1079 })
1080 );
1081 assert_eq!(
1082 options.next(),
1083 Some(DhcpOption {
1084 kind: field::OPT_PARAMETER_REQUEST_LIST,
1085 data: &[1, 3, 6, 42]
1086 })
1087 );
1088 assert_eq!(options.next(), None);
1089 }
1090
1091 #[test]
1092 fn test_construct_discover() {
1093 let mut bytes = vec![0xa5; 276];
1094 let mut packet = Packet::new_unchecked(&mut bytes);
1095 packet.set_magic_number(MAGIC_COOKIE);
1096 packet.set_sname_and_boot_file_to_zero();
1097 packet.set_opcode(OpCode::Request);
1098 packet.set_hardware_type(Hardware::Ethernet);
1099 packet.set_hardware_len(6);
1100 packet.set_hops(0);
1101 packet.set_transaction_id(0x3d1d);
1102 packet.set_secs(0);
1103 packet.set_flags(Flags::empty());
1104 packet.set_client_ip(IP_NULL);
1105 packet.set_your_ip(IP_NULL);
1106 packet.set_server_ip(IP_NULL);
1107 packet.set_relay_agent_ip(IP_NULL);
1108 packet.set_client_hardware_address(CLIENT_MAC);
1109
1110 let mut options = packet.options_mut();
1111
1112 options
1113 .emit(DhcpOption {
1114 kind: field::OPT_DHCP_MESSAGE_TYPE,
1115 data: &[0x01],
1116 })
1117 .unwrap();
1118 options
1119 .emit(DhcpOption {
1120 kind: field::OPT_CLIENT_ID,
1121 data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1122 })
1123 .unwrap();
1124 options
1125 .emit(DhcpOption {
1126 kind: field::OPT_REQUESTED_IP,
1127 data: &[0x00, 0x00, 0x00, 0x00],
1128 })
1129 .unwrap();
1130 options
1131 .emit(DhcpOption {
1132 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1133 data: &DHCP_SIZE.to_be_bytes(),
1134 })
1135 .unwrap();
1136 options
1137 .emit(DhcpOption {
1138 kind: field::OPT_PARAMETER_REQUEST_LIST,
1139 data: &[1, 3, 6, 42],
1140 })
1141 .unwrap();
1142 options.end().unwrap();
1143
1144 let packet = &mut packet.into_inner()[..];
1145 for byte in &mut packet[269..276] {
1146 *byte = 0; }
1148
1149 assert_eq!(packet, DISCOVER_BYTES);
1150 }
1151
1152 const fn offer_repr() -> Repr<'static> {
1153 Repr {
1154 message_type: MessageType::Offer,
1155 transaction_id: 0x3d1d,
1156 client_hardware_address: CLIENT_MAC,
1157 client_ip: IP_NULL,
1158 your_ip: IP_NULL,
1159 server_ip: IP_NULL,
1160 router: Some(IP_NULL),
1161 subnet_mask: Some(IP_NULL),
1162 relay_agent_ip: IP_NULL,
1163 secs: 0,
1164 broadcast: false,
1165 requested_ip: None,
1166 client_identifier: Some(CLIENT_MAC),
1167 server_identifier: None,
1168 parameter_request_list: None,
1169 dns_servers: None,
1170 max_size: None,
1171 renew_duration: None,
1172 rebind_duration: None,
1173 lease_duration: Some(0xffff_ffff), additional_options: &[],
1175 }
1176 }
1177
1178 const fn discover_repr() -> Repr<'static> {
1179 Repr {
1180 message_type: MessageType::Discover,
1181 transaction_id: 0x3d1d,
1182 client_hardware_address: CLIENT_MAC,
1183 client_ip: IP_NULL,
1184 your_ip: IP_NULL,
1185 server_ip: IP_NULL,
1186 router: None,
1187 subnet_mask: None,
1188 relay_agent_ip: IP_NULL,
1189 broadcast: false,
1190 secs: 0,
1191 max_size: Some(DHCP_SIZE),
1192 renew_duration: None,
1193 rebind_duration: None,
1194 lease_duration: None,
1195 requested_ip: Some(IP_NULL),
1196 client_identifier: Some(CLIENT_MAC),
1197 server_identifier: None,
1198 parameter_request_list: Some(&[1, 3, 6, 42]),
1199 dns_servers: None,
1200 additional_options: &[],
1201 }
1202 }
1203
1204 #[test]
1205 fn test_parse_discover() {
1206 let packet = Packet::new_unchecked(DISCOVER_BYTES);
1207 let repr = Repr::parse(&packet).unwrap();
1208 assert_eq!(repr, discover_repr());
1209 }
1210
1211 #[test]
1212 fn test_emit_discover() {
1213 let repr = discover_repr();
1214 let mut bytes = vec![0xa5; repr.buffer_len()];
1215 let mut packet = Packet::new_unchecked(&mut bytes);
1216 repr.emit(&mut packet).unwrap();
1217 let packet = &*packet.into_inner();
1218 let packet_len = packet.len();
1219 assert_eq!(packet, &DISCOVER_BYTES[..packet_len]);
1220 for byte in &DISCOVER_BYTES[packet_len..] {
1221 assert_eq!(*byte, 0); }
1223 }
1224
1225 #[test]
1226 fn test_emit_offer() {
1227 let repr = offer_repr();
1228 let mut bytes = vec![0xa5; repr.buffer_len()];
1229 let mut packet = Packet::new_unchecked(&mut bytes);
1230 repr.emit(&mut packet).unwrap();
1231 }
1232
1233 #[test]
1234 fn test_emit_offer_dns() {
1235 let repr = {
1236 let mut repr = offer_repr();
1237 repr.dns_servers = Some(
1238 Vec::from_slice(&[
1239 Ipv4Address::new(163, 1, 74, 6),
1240 Ipv4Address::new(163, 1, 74, 7),
1241 Ipv4Address::new(163, 1, 74, 3),
1242 ])
1243 .unwrap(),
1244 );
1245 repr
1246 };
1247 let mut bytes = vec![0xa5; repr.buffer_len()];
1248 let mut packet = Packet::new_unchecked(&mut bytes);
1249 repr.emit(&mut packet).unwrap();
1250
1251 let packet = Packet::new_unchecked(&bytes);
1252 let repr_parsed = Repr::parse(&packet).unwrap();
1253
1254 assert_eq!(
1255 repr_parsed.dns_servers,
1256 Some(
1257 Vec::from_slice(&[
1258 Ipv4Address::new(163, 1, 74, 6),
1259 Ipv4Address::new(163, 1, 74, 7),
1260 Ipv4Address::new(163, 1, 74, 3),
1261 ])
1262 .unwrap()
1263 )
1264 );
1265 }
1266
1267 #[test]
1268 fn test_emit_dhcp_option() {
1269 static DATA: &[u8] = &[1, 3, 6];
1270 let dhcp_option = DhcpOption {
1271 kind: field::OPT_PARAMETER_REQUEST_LIST,
1272 data: DATA,
1273 };
1274
1275 let mut bytes = vec![0xa5; 5];
1276 let mut writer = DhcpOptionWriter::new(&mut bytes);
1277 writer.emit(dhcp_option).unwrap();
1278
1279 assert_eq!(
1280 &bytes[0..2],
1281 &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]
1282 );
1283 assert_eq!(&bytes[2..], DATA);
1284 }
1285
1286 #[test]
1287 fn test_parse_ack_dns_servers() {
1288 let packet = Packet::new_unchecked(ACK_DNS_SERVER_BYTES);
1289 let repr = Repr::parse(&packet).unwrap();
1290
1291 assert_eq!(
1295 repr.dns_servers,
1296 Some(
1297 Vec::from_slice(&[
1298 Ipv4Address::new(163, 1, 74, 6),
1299 Ipv4Address::new(163, 1, 74, 7),
1300 Ipv4Address::new(163, 1, 74, 3)
1301 ])
1302 .unwrap()
1303 )
1304 );
1305 }
1306
1307 #[test]
1308 fn test_parse_ack_lease_duration() {
1309 let packet = Packet::new_unchecked(ACK_LEASE_TIME_BYTES);
1310 let repr = Repr::parse(&packet).unwrap();
1311
1312 assert_eq!(repr.lease_duration, Some(598));
1315 }
1316}