smoltcp/wire/
dhcpv4.rs

1// See https://tools.ietf.org/html/rfc2131 for the DHCP specification.
2
3use 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    /// The possible opcodes of a DHCP packet.
20    pub enum OpCode(u8) {
21        Request = 1,
22        Reply = 2,
23    }
24}
25
26enum_with_unknown! {
27    /// The possible message types of a DHCP packet.
28    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/// A buffer for DHCP options.
61#[derive(Debug)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63pub struct DhcpOptionWriter<'a> {
64    /// The underlying buffer, directly from the DHCP packet representation.
65    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    /// Emit a  [`DhcpOption`] into a [`DhcpOptionWriter`].
74    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/// A representation of a single DHCP option.
106#[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/// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer.
114#[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    // Vendor Extensions
144    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    // IP Layer Parameters per Host
166    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    // IP Layer Parameters per Interface
175    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    // Link Layer Parameters per Interface
185    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    // TCP Parameters
190    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    // Application and Service Parameters
195    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    // DHCP Extensions
218    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    /// Imbue a raw octet buffer with DHCP packet structure.
236    pub const fn new_unchecked(buffer: T) -> Packet<T> {
237        Packet { buffer }
238    }
239
240    /// Shorthand for a combination of [new_unchecked] and [check_len].
241    ///
242    /// [new_unchecked]: #method.new_unchecked
243    /// [check_len]: #method.check_len
244    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    /// Ensure that no accessor method will panic if called.
251    /// Returns `Err(Error)` if the buffer is too short.
252    ///
253    /// [set_header_len]: #method.set_header_len
254    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    /// Consume the packet, returning the underlying buffer.
264    pub fn into_inner(self) -> T {
265        self.buffer
266    }
267
268    /// Returns the operation code of this packet.
269    pub fn opcode(&self) -> OpCode {
270        let data = self.buffer.as_ref();
271        OpCode::from(data[field::OP])
272    }
273
274    /// Returns the hardware protocol type (e.g. ethernet).
275    pub fn hardware_type(&self) -> Hardware {
276        let data = self.buffer.as_ref();
277        Hardware::from(u16::from(data[field::HTYPE]))
278    }
279
280    /// Returns the length of a hardware address in bytes (e.g. 6 for ethernet).
281    pub fn hardware_len(&self) -> u8 {
282        self.buffer.as_ref()[field::HLEN]
283    }
284
285    /// Returns the transaction ID.
286    ///
287    /// The transaction ID (called `xid` in the specification) is a random number used to
288    /// associate messages and responses between client and server. The number is chosen by
289    /// the client.
290    pub fn transaction_id(&self) -> u32 {
291        let field = &self.buffer.as_ref()[field::XID];
292        NetworkEndian::read_u32(field)
293    }
294
295    /// Returns the hardware address of the client (called `chaddr` in the specification).
296    ///
297    /// Only ethernet is supported by `smoltcp`, so this functions returns
298    /// an `EthernetAddress`.
299    pub fn client_hardware_address(&self) -> EthernetAddress {
300        let field = &self.buffer.as_ref()[field::CHADDR];
301        EthernetAddress::from_bytes(field)
302    }
303
304    /// Returns the value of the `hops` field.
305    ///
306    /// The `hops` field is set to zero by clients and optionally used by relay agents.
307    pub fn hops(&self) -> u8 {
308        self.buffer.as_ref()[field::HOPS]
309    }
310
311    /// Returns the value of the `secs` field.
312    ///
313    /// The secs field is filled by clients and describes the number of seconds elapsed
314    /// since client began process.
315    pub fn secs(&self) -> u16 {
316        let field = &self.buffer.as_ref()[field::SECS];
317        NetworkEndian::read_u16(field)
318    }
319
320    /// Returns the value of the `magic cookie` field in the DHCP options.
321    ///
322    /// This field should be always be `0x63825363`.
323    pub fn magic_number(&self) -> u32 {
324        let field = &self.buffer.as_ref()[field::MAGIC_NUMBER];
325        NetworkEndian::read_u32(field)
326    }
327
328    /// Returns the Ipv4 address of the client, zero if not set.
329    ///
330    /// This corresponds to the `ciaddr` field in the DHCP specification. According to it,
331    /// this field is “only filled in if client is in `BOUND`, `RENEW` or `REBINDING` state
332    /// and can respond to ARP requests”.
333    pub fn client_ip(&self) -> Ipv4Address {
334        let field = &self.buffer.as_ref()[field::CIADDR];
335        Ipv4Address::from_bytes(field)
336    }
337
338    /// Returns the value of the `yiaddr` field, zero if not set.
339    pub fn your_ip(&self) -> Ipv4Address {
340        let field = &self.buffer.as_ref()[field::YIADDR];
341        Ipv4Address::from_bytes(field)
342    }
343
344    /// Returns the value of the `siaddr` field, zero if not set.
345    pub fn server_ip(&self) -> Ipv4Address {
346        let field = &self.buffer.as_ref()[field::SIADDR];
347        Ipv4Address::from_bytes(field)
348    }
349
350    /// Returns the value of the `giaddr` field, zero if not set.
351    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    /// Return an iterator over the options.
362    #[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                    // No more options, return.
369                    None => return None,
370                    Some(field::OPT_END) => return None,
371
372                    // Skip padding.
373                    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    /// Sets the optional `sname` (“server name”) and `file` (“boot file name”) fields to zero.
422    ///
423    /// The fields are not commonly used, so we set their value always to zero. **This method
424    /// must be called when creating a packet, otherwise the emitted values for these fields
425    /// are undefined!**
426    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    /// Sets the `OpCode` for the packet.
437    pub fn set_opcode(&mut self, value: OpCode) {
438        let data = self.buffer.as_mut();
439        data[field::OP] = value.into();
440    }
441
442    /// Sets the hardware address type (only ethernet is supported).
443    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)); // TODO: Replace with TryFrom when it's stable
447        data[field::HTYPE] = number as u8;
448    }
449
450    /// Sets the hardware address length.
451    ///
452    /// Only ethernet is supported, so this field should be set to the value `6`.
453    pub fn set_hardware_len(&mut self, value: u8) {
454        self.buffer.as_mut()[field::HLEN] = value;
455    }
456
457    /// Sets the transaction ID.
458    ///
459    /// The transaction ID (called `xid` in the specification) is a random number used to
460    /// associate messages and responses between client and server. The number is chosen by
461    /// the client.
462    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    /// Sets the ethernet address of the client.
468    ///
469    /// Sets the `chaddr` field.
470    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    /// Sets the hops field.
476    ///
477    /// The `hops` field is set to zero by clients and optionally used by relay agents.
478    pub fn set_hops(&mut self, value: u8) {
479        self.buffer.as_mut()[field::HOPS] = value;
480    }
481
482    /// Sets the `secs` field.
483    ///
484    /// The secs field is filled by clients and describes the number of seconds elapsed
485    /// since client began process.
486    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    /// Sets the value of the `magic cookie` field in the DHCP options.
492    ///
493    /// This field should be always be `0x63825363`.
494    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    /// Sets the Ipv4 address of the client.
500    ///
501    /// This corresponds to the `ciaddr` field in the DHCP specification. According to it,
502    /// this field is “only filled in if client is in `BOUND`, `RENEW` or `REBINDING` state
503    /// and can respond to ARP requests”.
504    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    /// Sets the value of the `yiaddr` field.
510    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    /// Sets the value of the `siaddr` field.
516    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    /// Sets the value of the `giaddr` field.
522    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    /// Sets the flags to the specified value.
528    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    /// Return a pointer to the options.
536    #[inline]
537    pub fn options_mut(&mut self) -> DhcpOptionWriter<'_> {
538        DhcpOptionWriter::new(&mut self.buffer.as_mut()[field::OPTIONS])
539    }
540}
541
542/// A high-level representation of a Dynamic Host Configuration Protocol packet.
543///
544/// DHCP messages have the following layout (see [RFC 2131](https://tools.ietf.org/html/rfc2131)
545/// for details):
546///
547/// ```no_rust
548/// 0                   1                   2                   3
549/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
550/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
551/// | message_type  | htype (N/A)   |   hlen (N/A)  |   hops        |
552/// +---------------+---------------+---------------+---------------+
553/// |                       transaction_id                          |
554/// +-------------------------------+-------------------------------+
555/// |           secs                |           flags               |
556/// +-------------------------------+-------------------------------+
557/// |                           client_ip                           |
558/// +---------------------------------------------------------------+
559/// |                            your_ip                            |
560/// +---------------------------------------------------------------+
561/// |                           server_ip                           |
562/// +---------------------------------------------------------------+
563/// |                        relay_agent_ip                         |
564/// +---------------------------------------------------------------+
565/// |                                                               |
566/// |                    client_hardware_address                    |
567/// |                                                               |
568/// |                                                               |
569/// +---------------------------------------------------------------+
570/// |                                                               |
571/// |                          sname  (N/A)                         |
572/// +---------------------------------------------------------------+
573/// |                                                               |
574/// |                          file    (N/A)                        |
575/// +---------------------------------------------------------------+
576/// |                                                               |
577/// |                          options                              |
578/// +---------------------------------------------------------------+
579/// ```
580///
581/// It is assumed that the access layer is Ethernet, so `htype` (the field representing the
582/// hardware address type) is always set to `1`, and `hlen` (which represents the hardware address
583/// length) is set to `6`.
584///
585/// The `options` field has a variable length.
586#[derive(Debug, PartialEq, Eq, Clone)]
587#[cfg_attr(feature = "defmt", derive(defmt::Format))]
588pub struct Repr<'a> {
589    /// This field is also known as `op` in the RFC. It indicates the type of DHCP message this
590    /// packet represents.
591    pub message_type: MessageType,
592    /// This field is also known as `xid` in the RFC. It is a random number chosen by the client,
593    /// used by the client and server to associate messages and responses between a client and a
594    /// server.
595    pub transaction_id: u32,
596    /// seconds elapsed since client began address acquisition or renewal
597    /// process the DHCPREQUEST message MUST use the same value in the DHCP
598    /// message header's 'secs' field and be sent to the same IP broadcast
599    /// address as the original DHCPDISCOVER message.
600    pub secs: u16,
601    /// This field is also known as `chaddr` in the RFC and for networks where the access layer is
602    /// ethernet, it is the client MAC address.
603    pub client_hardware_address: EthernetAddress,
604    /// This field is also known as `ciaddr` in the RFC. It is only filled in if client is in
605    /// BOUND, RENEW or REBINDING state and can respond to ARP requests.
606    pub client_ip: Ipv4Address,
607    /// This field is also known as `yiaddr` in the RFC.
608    pub your_ip: Ipv4Address,
609    /// This field is also known as `siaddr` in the RFC. It may be set by the server in DHCPOFFER
610    /// and DHCPACK messages, and represent the address of the next server to use in bootstrap.
611    pub server_ip: Ipv4Address,
612    /// Default gateway
613    pub router: Option<Ipv4Address>,
614    /// This field comes from a corresponding DhcpOption.
615    pub subnet_mask: Option<Ipv4Address>,
616    /// This field is also known as `giaddr` in the RFC. In order to allow DHCP clients on subnets
617    /// not directly served by DHCP servers to communicate with DHCP servers, DHCP relay agents can
618    /// be installed on these subnets. The DHCP client broadcasts on the local link; the relay
619    /// agent receives the broadcast and transmits it to one or more DHCP servers using unicast.
620    /// The relay agent stores its own IP address in the `relay_agent_ip` field of the DHCP packet.
621    /// The DHCP server uses the `relay_agent_ip` to determine the subnet on which the relay agent
622    /// received the broadcast, and allocates an IP address on that subnet. When the DHCP server
623    /// replies to the client, it sends the reply to the `relay_agent_ip` address, again using
624    /// unicast. The relay agent then retransmits the response on the local network
625    pub relay_agent_ip: Ipv4Address,
626    /// Broadcast flags. It can be set in DHCPDISCOVER, DHCPINFORM and DHCPREQUEST message if the
627    /// client requires the response to be broadcasted.
628    pub broadcast: bool,
629    /// The "requested IP address" option. It can be used by clients in DHCPREQUEST or DHCPDISCOVER
630    /// messages, or by servers in DHCPDECLINE messages.
631    pub requested_ip: Option<Ipv4Address>,
632    /// The "client identifier" option.
633    ///
634    /// The 'client identifier' is an opaque key, not to be interpreted by the server; for example,
635    /// the 'client identifier' may contain a hardware address, identical to the contents of the
636    /// 'chaddr' field, or it may contain another type of identifier, such as a DNS name.  The
637    /// 'client identifier' chosen by a DHCP client MUST be unique to that client within the subnet
638    /// to which the client is attached. If the client uses a 'client identifier' in one message,
639    /// it MUST use that same identifier in all subsequent messages, to ensure that all servers
640    /// correctly identify the client.
641    pub client_identifier: Option<EthernetAddress>,
642    /// The "server identifier" option. It is used both to identify a DHCP server
643    /// in a DHCP message and as a destination address from clients to servers.
644    pub server_identifier: Option<Ipv4Address>,
645    /// The parameter request list informs the server about which configuration parameters
646    /// the client is interested in.
647    pub parameter_request_list: Option<&'a [u8]>,
648    /// DNS servers
649    pub dns_servers: Option<Vec<Ipv4Address, MAX_DNS_SERVER_COUNT>>,
650    /// The maximum size dhcp packet the interface can receive
651    pub max_size: Option<u16>,
652    /// The DHCP IP lease duration, specified in seconds.
653    pub lease_duration: Option<u32>,
654    /// The DHCP IP renew duration (T1 interval), in seconds, if specified in the packet.
655    pub renew_duration: Option<u32>,
656    /// The DHCP IP rebind duration (T2 interval), in seconds, if specified in the packet.
657    pub rebind_duration: Option<u32>,
658    /// When returned from [`Repr::parse`], this field will be `None`.
659    /// However, when calling [`Repr::emit`], this field should contain only
660    /// additional DHCP options not known to smoltcp.
661    pub additional_options: &'a [DhcpOption<'a>],
662}
663
664impl<'a> Repr<'a> {
665    /// Return the length of a packet that will be emitted from this high-level representation.
666    pub fn buffer_len(&self) -> usize {
667        let mut len = field::OPTIONS.start;
668        // message type and end-of-options options
669        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    /// Parse a DHCP packet and return a high-level representation.
706    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        // only ethernet is supported right now
720        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), // unimplemented
727        }
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                        // We ignore push failures because that will only happen
795                        // if we attempt to push more than 4 addresses, and the only
796                        // solution to that is to support more addresses.
797                        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    /// Emit a high-level representation into a Dynamic Host
837    /// Configuration Protocol packet.
838    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; // padding bytes
1147        }
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), // Infinite lease
1174            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); // padding bytes
1222        }
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        // The packet described by ACK_BYTES advertises 4 DNS servers
1292        // Here we ensure that we correctly parse the first 3 into our fixed
1293        // length-3 array (see issue #305)
1294        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        // Verify that the lease time in the ACK is properly parsed. The packet contains a lease
1313        // duration of 598s.
1314        assert_eq!(repr.lease_duration, Some(598));
1315    }
1316}