nex_packet/
dhcp.rs

1use bytes::{Buf, BufMut, Bytes, BytesMut};
2use nex_core::mac::MacAddr;
3use std::net::Ipv4Addr;
4
5use crate::packet::{GenericMutablePacket, Packet};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10/// Minimum size of an DHCP packet.
11/// Options field is not included in this size.
12pub const DHCP_MIN_PACKET_SIZE: usize = 236;
13
14// DHCP Operation Codes
15#[repr(u8)]
16#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub enum DhcpOperation {
19    Request = 1,
20    Reply = 2,
21    Unknown(u8),
22}
23
24impl DhcpOperation {
25    pub fn new(value: u8) -> Self {
26        match value {
27            1 => Self::Request,
28            2 => Self::Reply,
29            other => Self::Unknown(other),
30        }
31    }
32
33    pub fn value(&self) -> u8 {
34        match self {
35            Self::Request => 1,
36            Self::Reply => 2,
37            Self::Unknown(v) => *v,
38        }
39    }
40}
41
42// DHCP Hardware Types
43#[repr(u8)]
44#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46pub enum DhcpHardwareType {
47    Ethernet = 1,
48    ExperimentalEthernet = 2,
49    AmateurRadioAX25 = 3,
50    ProteonProNETTokenRing = 4,
51    Chaos = 5,
52    IEEE802Networks = 6,
53    ARCNET = 7,
54    Hyperchannel = 8,
55    Lanstar = 9,
56    AutonetShortAddress = 10,
57    LocalTalk = 11,
58    LocalNet = 12,
59    UltraLink = 13,
60    SMDS = 14,
61    FrameRelay = 15,
62    ATM = 16,
63    HDLC = 17,
64    FibreChannel = 18,
65    ATM1 = 19,
66    PropPointToPointSerial = 20,
67    PPP = 21,
68    SoftwareLoopback = 24,
69    EON = 25,
70    Ethernet3MB = 26,
71    NSIP = 27,
72    Slip = 28,
73    ULTRALink = 29,
74    DS3 = 30,
75    SIP = 31,
76    FrameRelayInterconnect = 32,
77    ATM2 = 33,
78    MILSTD188220 = 34,
79    Metricom = 35,
80    IEEE1394 = 37,
81    MAPOS = 39,
82    Twinaxial = 40,
83    EUI64 = 41,
84    HIPARP = 42,
85    IPandARPoverISO7816_3 = 43,
86    ARPSec = 44,
87    IPsecTunnel = 45,
88    InfiniBand = 47,
89    TIA102CAI = 48,
90    WiegandInterface = 49,
91    PureIP = 50,
92    HWExp1 = 51,
93    HFI = 52,
94    HWExp2 = 53,
95    AEthernet = 54,
96    HWExp3 = 55,
97    IPsecTransport = 56,
98    SDLCRadio = 57,
99    SDLCMultipoint = 58,
100    IWARP = 59,
101    SixLoWPAN = 61,
102    VLAN = 62,
103    ProviderBridging = 63,
104    IEEE802154 = 64,
105    MAPOSinIPv4 = 65,
106    MAPOSinIPv6 = 66,
107    IEEE802154NonASKPHY = 70,
108    Unknown(u8),
109}
110
111impl DhcpHardwareType {
112    pub fn new(value: u8) -> Self {
113        use DhcpHardwareType::*;
114        match value {
115            1 => Ethernet,
116            2 => ExperimentalEthernet,
117            3 => AmateurRadioAX25,
118            4 => ProteonProNETTokenRing,
119            5 => Chaos,
120            6 => IEEE802Networks,
121            7 => ARCNET,
122            8 => Hyperchannel,
123            9 => Lanstar,
124            10 => AutonetShortAddress,
125            11 => LocalTalk,
126            12 => LocalNet,
127            13 => UltraLink,
128            14 => SMDS,
129            15 => FrameRelay,
130            16 => ATM,
131            17 => HDLC,
132            18 => FibreChannel,
133            19 => ATM1,
134            20 => PropPointToPointSerial,
135            21 => PPP,
136            24 => SoftwareLoopback,
137            25 => EON,
138            26 => Ethernet3MB,
139            27 => NSIP,
140            28 => Slip,
141            29 => ULTRALink,
142            30 => DS3,
143            31 => SIP,
144            32 => FrameRelayInterconnect,
145            33 => ATM2,
146            34 => MILSTD188220,
147            35 => Metricom,
148            37 => IEEE1394,
149            39 => MAPOS,
150            40 => Twinaxial,
151            41 => EUI64,
152            42 => HIPARP,
153            43 => IPandARPoverISO7816_3,
154            44 => ARPSec,
155            45 => IPsecTunnel,
156            47 => InfiniBand,
157            48 => TIA102CAI,
158            49 => WiegandInterface,
159            50 => PureIP,
160            51 => HWExp1,
161            52 => HFI,
162            53 => HWExp2,
163            54 => AEthernet,
164            55 => HWExp3,
165            56 => IPsecTransport,
166            57 => SDLCRadio,
167            58 => SDLCMultipoint,
168            59 => IWARP,
169            61 => SixLoWPAN,
170            62 => VLAN,
171            63 => ProviderBridging,
172            64 => IEEE802154,
173            65 => MAPOSinIPv4,
174            66 => MAPOSinIPv6,
175            70 => IEEE802154NonASKPHY,
176            other => Unknown(other),
177        }
178    }
179
180    pub fn value(&self) -> u8 {
181        match self {
182            DhcpHardwareType::Ethernet => 1,
183            DhcpHardwareType::ExperimentalEthernet => 2,
184            DhcpHardwareType::AmateurRadioAX25 => 3,
185            DhcpHardwareType::ProteonProNETTokenRing => 4,
186            DhcpHardwareType::Chaos => 5,
187            DhcpHardwareType::IEEE802Networks => 6,
188            DhcpHardwareType::ARCNET => 7,
189            DhcpHardwareType::Hyperchannel => 8,
190            DhcpHardwareType::Lanstar => 9,
191            DhcpHardwareType::AutonetShortAddress => 10,
192            DhcpHardwareType::LocalTalk => 11,
193            DhcpHardwareType::LocalNet => 12,
194            DhcpHardwareType::UltraLink => 13,
195            DhcpHardwareType::SMDS => 14,
196            DhcpHardwareType::FrameRelay => 15,
197            DhcpHardwareType::ATM => 16,
198            DhcpHardwareType::HDLC => 17,
199            DhcpHardwareType::FibreChannel => 18,
200            DhcpHardwareType::ATM1 => 19,
201            DhcpHardwareType::PropPointToPointSerial => 20,
202            DhcpHardwareType::PPP => 21,
203            DhcpHardwareType::SoftwareLoopback => 24,
204            DhcpHardwareType::EON => 25,
205            DhcpHardwareType::Ethernet3MB => 26,
206            DhcpHardwareType::NSIP => 27,
207            DhcpHardwareType::Slip => 28,
208            DhcpHardwareType::ULTRALink => 29,
209            DhcpHardwareType::DS3 => 30,
210            DhcpHardwareType::SIP => 31,
211            DhcpHardwareType::FrameRelayInterconnect => 32,
212            DhcpHardwareType::ATM2 => 33,
213            DhcpHardwareType::MILSTD188220 => 34,
214            DhcpHardwareType::Metricom => 35,
215            DhcpHardwareType::IEEE1394 => 37,
216            DhcpHardwareType::MAPOS => 39,
217            DhcpHardwareType::Twinaxial => 40,
218            DhcpHardwareType::EUI64 => 41,
219            DhcpHardwareType::HIPARP => 42,
220            DhcpHardwareType::IPandARPoverISO7816_3 => 43,
221            DhcpHardwareType::ARPSec => 44,
222            DhcpHardwareType::IPsecTunnel => 45,
223            DhcpHardwareType::InfiniBand => 47,
224            DhcpHardwareType::TIA102CAI => 48,
225            DhcpHardwareType::WiegandInterface => 49,
226            DhcpHardwareType::PureIP => 50,
227            DhcpHardwareType::HWExp1 => 51,
228            DhcpHardwareType::HFI => 52,
229            DhcpHardwareType::HWExp2 => 53,
230            DhcpHardwareType::AEthernet => 54,
231            DhcpHardwareType::HWExp3 => 55,
232            DhcpHardwareType::IPsecTransport => 56,
233            DhcpHardwareType::SDLCRadio => 57,
234            DhcpHardwareType::SDLCMultipoint => 58,
235            DhcpHardwareType::IWARP => 59,
236            DhcpHardwareType::SixLoWPAN => 61,
237            DhcpHardwareType::VLAN => 62,
238            DhcpHardwareType::ProviderBridging => 63,
239            DhcpHardwareType::IEEE802154 => 64,
240            DhcpHardwareType::MAPOSinIPv4 => 65,
241            DhcpHardwareType::MAPOSinIPv6 => 66,
242            DhcpHardwareType::IEEE802154NonASKPHY => 70,
243            DhcpHardwareType::Unknown(n) => *n,
244        }
245    }
246}
247
248#[derive(Clone, Debug, PartialEq, Eq)]
249#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
250pub struct DhcpHeader {
251    pub op: DhcpOperation,
252    pub htype: DhcpHardwareType,
253    pub hlen: u8,
254    pub hops: u8,
255    pub xid: u32,
256    pub secs: u16,
257    pub flags: u16,
258    pub ciaddr: Ipv4Addr,
259    pub yiaddr: Ipv4Addr,
260    pub siaddr: Ipv4Addr,
261    pub giaddr: Ipv4Addr,
262    pub chaddr: MacAddr,
263    /// Client hardware address padding (must be exactly 10 bytes during parsing/building)
264    pub chaddr_pad: Vec<u8>,
265    /// Optional server host name (must be exactly 64 bytes during parsing/building)
266    pub sname: Vec<u8>,
267    /// Boot file name (must be exactly 128 bytes during parsing/building)
268    pub file: Vec<u8>,
269}
270
271#[derive(Clone, Debug, PartialEq, Eq)]
272pub struct DhcpPacket {
273    pub header: DhcpHeader,
274    pub payload: Bytes,
275}
276
277impl Packet for DhcpPacket {
278    type Header = DhcpHeader;
279
280    fn from_buf(mut bytes: &[u8]) -> Option<Self> {
281        if bytes.len() < DHCP_MIN_PACKET_SIZE {
282            return None;
283        }
284
285        let op = DhcpOperation::new(bytes.get_u8());
286        let htype = DhcpHardwareType::new(bytes.get_u8());
287        let hlen = bytes.get_u8();
288        let hops = bytes.get_u8();
289        let xid = bytes.get_u32();
290        let secs = bytes.get_u16();
291        let flags = bytes.get_u16();
292
293        let ciaddr = Ipv4Addr::from(bytes.get_u32());
294        let yiaddr = Ipv4Addr::from(bytes.get_u32());
295        let siaddr = Ipv4Addr::from(bytes.get_u32());
296        let giaddr = Ipv4Addr::from(bytes.get_u32());
297
298        let mut chaddr = [0u8; 6];
299        bytes.copy_to_slice(&mut chaddr);
300        let chaddr = MacAddr::from_octets(chaddr);
301
302        let mut chaddr_pad = [0u8; 10];
303        bytes.copy_to_slice(&mut chaddr_pad);
304
305        let mut sname = [0u8; 64];
306        bytes.copy_to_slice(&mut sname);
307
308        let mut file = [0u8; 128];
309        bytes.copy_to_slice(&mut file);
310
311        let header = DhcpHeader {
312            op,
313            htype,
314            hlen,
315            hops,
316            xid,
317            secs,
318            flags,
319            ciaddr,
320            yiaddr,
321            siaddr,
322            giaddr,
323            chaddr,
324            chaddr_pad: chaddr_pad.to_vec(),
325            sname: sname.to_vec(),
326            file: file.to_vec(),
327        };
328
329        Some(Self {
330            header,
331            payload: Bytes::copy_from_slice(bytes),
332        })
333    }
334
335    fn from_bytes(bytes: Bytes) -> Option<Self> {
336        Self::from_buf(&bytes)
337    }
338
339    fn to_bytes(&self) -> Bytes {
340        let mut buf = BytesMut::with_capacity(DHCP_MIN_PACKET_SIZE + self.payload.len());
341
342        buf.put_u8(self.header.op.value());
343        buf.put_u8(self.header.htype.value());
344        buf.put_u8(self.header.hlen);
345        buf.put_u8(self.header.hops);
346        buf.put_u32(self.header.xid);
347        buf.put_u16(self.header.secs);
348        buf.put_u16(self.header.flags);
349
350        buf.put_slice(&self.header.ciaddr.octets());
351        buf.put_slice(&self.header.yiaddr.octets());
352        buf.put_slice(&self.header.siaddr.octets());
353        buf.put_slice(&self.header.giaddr.octets());
354
355        buf.put_slice(&self.header.chaddr.octets());
356        buf.put_slice(&self.header.chaddr_pad);
357        buf.put_slice(&self.header.sname);
358        buf.put_slice(&self.header.file);
359
360        buf.extend_from_slice(&self.payload);
361
362        buf.freeze()
363    }
364    fn header(&self) -> Bytes {
365        let mut buf = BytesMut::with_capacity(DHCP_MIN_PACKET_SIZE);
366
367        buf.put_u8(self.header.op.value());
368        buf.put_u8(self.header.htype.value());
369        buf.put_u8(self.header.hlen);
370        buf.put_u8(self.header.hops);
371        buf.put_u32(self.header.xid);
372        buf.put_u16(self.header.secs);
373        buf.put_u16(self.header.flags);
374
375        buf.put_slice(&self.header.ciaddr.octets());
376        buf.put_slice(&self.header.yiaddr.octets());
377        buf.put_slice(&self.header.siaddr.octets());
378        buf.put_slice(&self.header.giaddr.octets());
379
380        buf.put_slice(&self.header.chaddr.octets());
381        buf.put_slice(&self.header.chaddr_pad);
382        buf.put_slice(&self.header.sname);
383        buf.put_slice(&self.header.file);
384
385        buf.freeze()
386    }
387
388    fn payload(&self) -> Bytes {
389        self.payload.clone()
390    }
391
392    fn header_len(&self) -> usize {
393        DHCP_MIN_PACKET_SIZE
394    }
395
396    fn payload_len(&self) -> usize {
397        self.payload.len()
398    }
399
400    fn total_len(&self) -> usize {
401        self.header_len() + self.payload_len()
402    }
403
404    fn into_parts(self) -> (Self::Header, Bytes) {
405        (self.header, self.payload)
406    }
407}
408
409/// Represents a mutable DHCP packet.
410pub type MutableDhcpPacket<'a> = GenericMutablePacket<'a, DhcpPacket>;
411
412#[cfg(test)]
413mod tests {
414    use super::*;
415    use crate::packet::MutablePacket;
416    use nex_core::mac::MacAddr;
417
418    #[test]
419    fn test_dhcp_packet_from_bytes_and_to_bytes() {
420        let raw = {
421            let mut buf = BytesMut::with_capacity(DHCP_MIN_PACKET_SIZE);
422            buf.put_u8(1); // op: Request
423            buf.put_u8(1); // htype: Ethernet
424            buf.put_u8(6); // hlen
425            buf.put_u8(0); // hops
426            buf.put_u32(0x12345678); // xid
427            buf.put_u16(0); // secs
428            buf.put_u16(0); // flags
429            buf.put_slice(&[0, 0, 0, 0]); // ciaddr
430            buf.put_slice(&[0, 0, 0, 0]); // yiaddr
431            buf.put_slice(&[0, 0, 0, 0]); // siaddr
432            buf.put_slice(&[0, 0, 0, 0]); // giaddr
433            buf.put_slice(&[0x00, 0x11, 0x22, 0x33, 0x44, 0x55]); // chaddr
434            buf.extend_from_slice(&[0u8; 10]); // chaddr_pad
435            buf.extend_from_slice(&[0u8; 64]); // sname
436            buf.extend_from_slice(&[0u8; 128]); // file
437            buf.freeze()
438        };
439
440        let packet = DhcpPacket::from_bytes(raw.clone()).expect("Failed to parse DHCP packet");
441
442        assert_eq!(packet.header.op, DhcpOperation::Request);
443        assert_eq!(packet.header.htype, DhcpHardwareType::Ethernet);
444        assert_eq!(packet.header.hlen, 6);
445        assert_eq!(packet.header.xid, 0x12345678);
446        assert_eq!(
447            packet.header.chaddr,
448            MacAddr::new(0x00, 0x11, 0x22, 0x33, 0x44, 0x55)
449        );
450
451        let rebuilt = packet.to_bytes();
452        assert_eq!(rebuilt, raw);
453    }
454
455    #[test]
456    fn test_mutable_dhcp_packet_alias() {
457        let mut raw = [0u8; DHCP_MIN_PACKET_SIZE + 4];
458        raw[0] = DhcpOperation::Request.value();
459        raw[1] = DhcpHardwareType::Ethernet.value();
460        raw[2] = 6; // hardware length
461
462        let mut packet = <MutableDhcpPacket as MutablePacket>::new(&mut raw).expect("mutable dhcp");
463        packet.header_mut()[0] = DhcpOperation::Reply.value();
464        packet.payload_mut()[0] = 0xaa;
465
466        let frozen = packet.freeze().expect("freeze");
467        assert_eq!(frozen.header.op, DhcpOperation::Reply);
468        assert_eq!(frozen.payload[0], 0xaa);
469    }
470}