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