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};
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_value())); 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.as_bytes());
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.as_bytes());
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.as_bytes());
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.as_bytes());
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 let transaction_id = packet.transaction_id();
711 let client_hardware_address = packet.client_hardware_address();
712 let client_ip = packet.client_ip();
713 let your_ip = packet.your_ip();
714 let server_ip = packet.server_ip();
715 let relay_agent_ip = packet.relay_agent_ip();
716 let secs = packet.secs();
717
718 match packet.hardware_type() {
720 Hardware::Ethernet => {
721 if packet.hardware_len() != 6 {
722 return Err(Error);
723 }
724 }
725 Hardware::Unknown(_) => return Err(Error), }
727
728 if packet.magic_number() != DHCP_MAGIC_NUMBER {
729 return Err(Error);
730 }
731
732 let mut message_type = Err(Error);
733 let mut requested_ip = None;
734 let mut client_identifier = None;
735 let mut server_identifier = None;
736 let mut router = None;
737 let mut subnet_mask = None;
738 let mut parameter_request_list = None;
739 let mut dns_servers = None;
740 let mut max_size = None;
741 let mut lease_duration = None;
742 let mut renew_duration = None;
743 let mut rebind_duration = None;
744
745 for option in packet.options() {
746 let data = option.data;
747 match (option.kind, data.len()) {
748 (field::OPT_DHCP_MESSAGE_TYPE, 1) => {
749 let value = MessageType::from(data[0]);
750 if value.opcode() == packet.opcode() {
751 message_type = Ok(value);
752 }
753 }
754 (field::OPT_REQUESTED_IP, 4) => {
755 requested_ip = Some(Ipv4Address::from_bytes(data));
756 }
757 (field::OPT_CLIENT_ID, 7) => {
758 let hardware_type = Hardware::from(u16::from(data[0]));
759 if hardware_type != Hardware::Ethernet {
760 return Err(Error);
761 }
762 client_identifier = Some(EthernetAddress::from_bytes(&data[1..]));
763 }
764 (field::OPT_SERVER_IDENTIFIER, 4) => {
765 server_identifier = Some(Ipv4Address::from_bytes(data));
766 }
767 (field::OPT_ROUTER, 4) => {
768 router = Some(Ipv4Address::from_bytes(data));
769 }
770 (field::OPT_SUBNET_MASK, 4) => {
771 subnet_mask = Some(Ipv4Address::from_bytes(data));
772 }
773 (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
774 max_size = Some(u16::from_be_bytes([data[0], data[1]]));
775 }
776 (field::OPT_RENEWAL_TIME_VALUE, 4) => {
777 renew_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
778 }
779 (field::OPT_REBINDING_TIME_VALUE, 4) => {
780 rebind_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
781 }
782 (field::OPT_IP_LEASE_TIME, 4) => {
783 lease_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
784 }
785 (field::OPT_PARAMETER_REQUEST_LIST, _) => {
786 parameter_request_list = Some(data);
787 }
788 (field::OPT_DOMAIN_NAME_SERVER, _) => {
789 let mut servers = Vec::new();
790 const IP_ADDR_BYTE_LEN: usize = 4;
791 let mut addrs = data.chunks_exact(IP_ADDR_BYTE_LEN);
792 for chunk in &mut addrs {
793 servers.push(Ipv4Address::from_bytes(chunk)).ok();
797 }
798 dns_servers = Some(servers);
799
800 if !addrs.remainder().is_empty() {
801 net_trace!("DHCP domain name servers contained invalid address");
802 }
803 }
804 _ => {}
805 }
806 }
807
808 let broadcast = packet.flags().contains(Flags::BROADCAST);
809
810 Ok(Repr {
811 secs,
812 transaction_id,
813 client_hardware_address,
814 client_ip,
815 your_ip,
816 server_ip,
817 relay_agent_ip,
818 broadcast,
819 requested_ip,
820 server_identifier,
821 router,
822 subnet_mask,
823 client_identifier,
824 parameter_request_list,
825 dns_servers,
826 max_size,
827 lease_duration,
828 renew_duration,
829 rebind_duration,
830 message_type: message_type?,
831 additional_options: &[],
832 })
833 }
834
835 pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
838 where
839 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
840 {
841 packet.set_sname_and_boot_file_to_zero();
842 packet.set_opcode(self.message_type.opcode());
843 packet.set_hardware_type(Hardware::Ethernet);
844 packet.set_hardware_len(6);
845 packet.set_transaction_id(self.transaction_id);
846 packet.set_client_hardware_address(self.client_hardware_address);
847 packet.set_hops(0);
848 packet.set_secs(self.secs);
849 packet.set_magic_number(0x63825363);
850 packet.set_client_ip(self.client_ip);
851 packet.set_your_ip(self.your_ip);
852 packet.set_server_ip(self.server_ip);
853 packet.set_relay_agent_ip(self.relay_agent_ip);
854
855 let mut flags = Flags::empty();
856 if self.broadcast {
857 flags |= Flags::BROADCAST;
858 }
859 packet.set_flags(flags);
860
861 {
862 let mut options = packet.options_mut();
863
864 options.emit(DhcpOption {
865 kind: field::OPT_DHCP_MESSAGE_TYPE,
866 data: &[self.message_type.into()],
867 })?;
868
869 if let Some(val) = &self.client_identifier {
870 let mut data = [0; 7];
871 data[0] = u16::from(Hardware::Ethernet) as u8;
872 data[1..].copy_from_slice(val.as_bytes());
873
874 options.emit(DhcpOption {
875 kind: field::OPT_CLIENT_ID,
876 data: &data,
877 })?;
878 }
879
880 if let Some(val) = &self.server_identifier {
881 options.emit(DhcpOption {
882 kind: field::OPT_SERVER_IDENTIFIER,
883 data: val.as_bytes(),
884 })?;
885 }
886
887 if let Some(val) = &self.router {
888 options.emit(DhcpOption {
889 kind: field::OPT_ROUTER,
890 data: val.as_bytes(),
891 })?;
892 }
893 if let Some(val) = &self.subnet_mask {
894 options.emit(DhcpOption {
895 kind: field::OPT_SUBNET_MASK,
896 data: val.as_bytes(),
897 })?;
898 }
899 if let Some(val) = &self.requested_ip {
900 options.emit(DhcpOption {
901 kind: field::OPT_REQUESTED_IP,
902 data: val.as_bytes(),
903 })?;
904 }
905 if let Some(val) = &self.max_size {
906 options.emit(DhcpOption {
907 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
908 data: &val.to_be_bytes(),
909 })?;
910 }
911 if let Some(val) = &self.lease_duration {
912 options.emit(DhcpOption {
913 kind: field::OPT_IP_LEASE_TIME,
914 data: &val.to_be_bytes(),
915 })?;
916 }
917 if let Some(val) = &self.parameter_request_list {
918 options.emit(DhcpOption {
919 kind: field::OPT_PARAMETER_REQUEST_LIST,
920 data: val,
921 })?;
922 }
923
924 if let Some(dns_servers) = &self.dns_servers {
925 const IP_SIZE: usize = core::mem::size_of::<u32>();
926 let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE];
927
928 let data_len = dns_servers
929 .iter()
930 .enumerate()
931 .inspect(|(i, ip)| {
932 servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(ip.as_bytes());
933 })
934 .count()
935 * IP_SIZE;
936 options.emit(DhcpOption {
937 kind: field::OPT_DOMAIN_NAME_SERVER,
938 data: &servers[..data_len],
939 })?;
940 }
941
942 for option in self.additional_options {
943 options.emit(*option)?;
944 }
945
946 options.end()?;
947 }
948
949 Ok(())
950 }
951}
952
953#[cfg(test)]
954mod test {
955 use super::*;
956 use crate::wire::Ipv4Address;
957
958 const MAGIC_COOKIE: u32 = 0x63825363;
959
960 static DISCOVER_BYTES: &[u8] = &[
961 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
962 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
963 0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
964 0x00, 0x00, 0x00, 0x00, 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, 0x63, 0x82, 0x53, 0x63,
977 0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00,
978 0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00,
979 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
980 ];
981
982 static ACK_DNS_SERVER_BYTES: &[u8] = &[
983 0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06,
984 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17,
985 0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
986 0x00, 0x00, 0x00, 0x00, 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, 0x63, 0x82, 0x53, 0x63,
999 0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00,
1000 0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68,
1001 0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00,
1002 0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a,
1003 0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03,
1004 0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08,
1005 0xff,
1006 ];
1007
1008 static ACK_LEASE_TIME_BYTES: &[u8] = &[
1009 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1010 0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91,
1011 0x62, 0xd2, 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1012 0x00, 0x00, 0x00, 0x00, 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, 0x63, 0x82, 0x53, 0x63,
1025 0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56,
1026 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00,
1027 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1028 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1029 ];
1030
1031 const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
1032 const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
1033 const DHCP_SIZE: u16 = 1500;
1034
1035 #[test]
1036 fn test_deconstruct_discover() {
1037 let packet = Packet::new_unchecked(DISCOVER_BYTES);
1038 assert_eq!(packet.magic_number(), MAGIC_COOKIE);
1039 assert_eq!(packet.opcode(), OpCode::Request);
1040 assert_eq!(packet.hardware_type(), Hardware::Ethernet);
1041 assert_eq!(packet.hardware_len(), 6);
1042 assert_eq!(packet.hops(), 0);
1043 assert_eq!(packet.transaction_id(), 0x3d1d);
1044 assert_eq!(packet.secs(), 0);
1045 assert_eq!(packet.client_ip(), IP_NULL);
1046 assert_eq!(packet.your_ip(), IP_NULL);
1047 assert_eq!(packet.server_ip(), IP_NULL);
1048 assert_eq!(packet.relay_agent_ip(), IP_NULL);
1049 assert_eq!(packet.client_hardware_address(), CLIENT_MAC);
1050
1051 let mut options = packet.options();
1052 assert_eq!(
1053 options.next(),
1054 Some(DhcpOption {
1055 kind: field::OPT_DHCP_MESSAGE_TYPE,
1056 data: &[0x01]
1057 })
1058 );
1059 assert_eq!(
1060 options.next(),
1061 Some(DhcpOption {
1062 kind: field::OPT_CLIENT_ID,
1063 data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1064 })
1065 );
1066 assert_eq!(
1067 options.next(),
1068 Some(DhcpOption {
1069 kind: field::OPT_REQUESTED_IP,
1070 data: &[0x00, 0x00, 0x00, 0x00],
1071 })
1072 );
1073 assert_eq!(
1074 options.next(),
1075 Some(DhcpOption {
1076 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1077 data: &DHCP_SIZE.to_be_bytes(),
1078 })
1079 );
1080 assert_eq!(
1081 options.next(),
1082 Some(DhcpOption {
1083 kind: field::OPT_PARAMETER_REQUEST_LIST,
1084 data: &[1, 3, 6, 42]
1085 })
1086 );
1087 assert_eq!(options.next(), None);
1088 }
1089
1090 #[test]
1091 fn test_construct_discover() {
1092 let mut bytes = vec![0xa5; 276];
1093 let mut packet = Packet::new_unchecked(&mut bytes);
1094 packet.set_magic_number(MAGIC_COOKIE);
1095 packet.set_sname_and_boot_file_to_zero();
1096 packet.set_opcode(OpCode::Request);
1097 packet.set_hardware_type(Hardware::Ethernet);
1098 packet.set_hardware_len(6);
1099 packet.set_hops(0);
1100 packet.set_transaction_id(0x3d1d);
1101 packet.set_secs(0);
1102 packet.set_flags(Flags::empty());
1103 packet.set_client_ip(IP_NULL);
1104 packet.set_your_ip(IP_NULL);
1105 packet.set_server_ip(IP_NULL);
1106 packet.set_relay_agent_ip(IP_NULL);
1107 packet.set_client_hardware_address(CLIENT_MAC);
1108
1109 let mut options = packet.options_mut();
1110
1111 options
1112 .emit(DhcpOption {
1113 kind: field::OPT_DHCP_MESSAGE_TYPE,
1114 data: &[0x01],
1115 })
1116 .unwrap();
1117 options
1118 .emit(DhcpOption {
1119 kind: field::OPT_CLIENT_ID,
1120 data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1121 })
1122 .unwrap();
1123 options
1124 .emit(DhcpOption {
1125 kind: field::OPT_REQUESTED_IP,
1126 data: &[0x00, 0x00, 0x00, 0x00],
1127 })
1128 .unwrap();
1129 options
1130 .emit(DhcpOption {
1131 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1132 data: &DHCP_SIZE.to_be_bytes(),
1133 })
1134 .unwrap();
1135 options
1136 .emit(DhcpOption {
1137 kind: field::OPT_PARAMETER_REQUEST_LIST,
1138 data: &[1, 3, 6, 42],
1139 })
1140 .unwrap();
1141 options.end().unwrap();
1142
1143 let packet = &mut packet.into_inner()[..];
1144 for byte in &mut packet[269..276] {
1145 *byte = 0; }
1147
1148 assert_eq!(packet, DISCOVER_BYTES);
1149 }
1150
1151 const fn offer_repr() -> Repr<'static> {
1152 Repr {
1153 message_type: MessageType::Offer,
1154 transaction_id: 0x3d1d,
1155 client_hardware_address: CLIENT_MAC,
1156 client_ip: IP_NULL,
1157 your_ip: IP_NULL,
1158 server_ip: IP_NULL,
1159 router: Some(IP_NULL),
1160 subnet_mask: Some(IP_NULL),
1161 relay_agent_ip: IP_NULL,
1162 secs: 0,
1163 broadcast: false,
1164 requested_ip: None,
1165 client_identifier: Some(CLIENT_MAC),
1166 server_identifier: None,
1167 parameter_request_list: None,
1168 dns_servers: None,
1169 max_size: None,
1170 renew_duration: None,
1171 rebind_duration: None,
1172 lease_duration: Some(0xffff_ffff), additional_options: &[],
1174 }
1175 }
1176
1177 const fn discover_repr() -> Repr<'static> {
1178 Repr {
1179 message_type: MessageType::Discover,
1180 transaction_id: 0x3d1d,
1181 client_hardware_address: CLIENT_MAC,
1182 client_ip: IP_NULL,
1183 your_ip: IP_NULL,
1184 server_ip: IP_NULL,
1185 router: None,
1186 subnet_mask: None,
1187 relay_agent_ip: IP_NULL,
1188 broadcast: false,
1189 secs: 0,
1190 max_size: Some(DHCP_SIZE),
1191 renew_duration: None,
1192 rebind_duration: None,
1193 lease_duration: None,
1194 requested_ip: Some(IP_NULL),
1195 client_identifier: Some(CLIENT_MAC),
1196 server_identifier: None,
1197 parameter_request_list: Some(&[1, 3, 6, 42]),
1198 dns_servers: None,
1199 additional_options: &[],
1200 }
1201 }
1202
1203 #[test]
1204 fn test_parse_discover() {
1205 let packet = Packet::new_unchecked(DISCOVER_BYTES);
1206 let repr = Repr::parse(&packet).unwrap();
1207 assert_eq!(repr, discover_repr());
1208 }
1209
1210 #[test]
1211 fn test_emit_discover() {
1212 let repr = discover_repr();
1213 let mut bytes = vec![0xa5; repr.buffer_len()];
1214 let mut packet = Packet::new_unchecked(&mut bytes);
1215 repr.emit(&mut packet).unwrap();
1216 let packet = &*packet.into_inner();
1217 let packet_len = packet.len();
1218 assert_eq!(packet, &DISCOVER_BYTES[..packet_len]);
1219 for byte in &DISCOVER_BYTES[packet_len..] {
1220 assert_eq!(*byte, 0); }
1222 }
1223
1224 #[test]
1225 fn test_emit_offer() {
1226 let repr = offer_repr();
1227 let mut bytes = vec![0xa5; repr.buffer_len()];
1228 let mut packet = Packet::new_unchecked(&mut bytes);
1229 repr.emit(&mut packet).unwrap();
1230 }
1231
1232 #[test]
1233 fn test_emit_offer_dns() {
1234 let repr = {
1235 let mut repr = offer_repr();
1236 repr.dns_servers = Some(
1237 Vec::from_slice(&[
1238 Ipv4Address([163, 1, 74, 6]),
1239 Ipv4Address([163, 1, 74, 7]),
1240 Ipv4Address([163, 1, 74, 3]),
1241 ])
1242 .unwrap(),
1243 );
1244 repr
1245 };
1246 let mut bytes = vec![0xa5; repr.buffer_len()];
1247 let mut packet = Packet::new_unchecked(&mut bytes);
1248 repr.emit(&mut packet).unwrap();
1249
1250 let packet = Packet::new_unchecked(&bytes);
1251 let repr_parsed = Repr::parse(&packet).unwrap();
1252
1253 assert_eq!(
1254 repr_parsed.dns_servers,
1255 Some(
1256 Vec::from_slice(&[
1257 Ipv4Address([163, 1, 74, 6]),
1258 Ipv4Address([163, 1, 74, 7]),
1259 Ipv4Address([163, 1, 74, 3]),
1260 ])
1261 .unwrap()
1262 )
1263 );
1264 }
1265
1266 #[test]
1267 fn test_emit_dhcp_option() {
1268 static DATA: &[u8] = &[1, 3, 6];
1269 let dhcp_option = DhcpOption {
1270 kind: field::OPT_PARAMETER_REQUEST_LIST,
1271 data: DATA,
1272 };
1273
1274 let mut bytes = vec![0xa5; 5];
1275 let mut writer = DhcpOptionWriter::new(&mut bytes);
1276 writer.emit(dhcp_option).unwrap();
1277
1278 assert_eq!(
1279 &bytes[0..2],
1280 &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]
1281 );
1282 assert_eq!(&bytes[2..], DATA);
1283 }
1284
1285 #[test]
1286 fn test_parse_ack_dns_servers() {
1287 let packet = Packet::new_unchecked(ACK_DNS_SERVER_BYTES);
1288 let repr = Repr::parse(&packet).unwrap();
1289
1290 assert_eq!(
1294 repr.dns_servers,
1295 Some(
1296 Vec::from_slice(&[
1297 Ipv4Address([163, 1, 74, 6]),
1298 Ipv4Address([163, 1, 74, 7]),
1299 Ipv4Address([163, 1, 74, 3])
1300 ])
1301 .unwrap()
1302 )
1303 );
1304 }
1305
1306 #[test]
1307 fn test_parse_ack_lease_duration() {
1308 let packet = Packet::new_unchecked(ACK_LEASE_TIME_BYTES);
1309 let repr = Repr::parse(&packet).unwrap();
1310
1311 assert_eq!(repr.lease_duration, Some(598));
1314 }
1315}