Skip to main content

wireforge_app/dhcp/
builder.rs

1//! DHCPv4 message builder (RFC 2131/2132).
2
3use super::parser::DHCP_MIN_LEN;
4use super::types::DhcpOption;
5
6const DHCP_MAGIC_COOKIE: u32 = 0x63825363;
7
8pub struct DhcpPacketBuilder {
9    buf: Vec<u8>,
10    options: Vec<u8>,
11}
12
13impl Default for DhcpPacketBuilder {
14    fn default() -> Self { Self::new() }
15}
16
17impl DhcpPacketBuilder {
18    pub fn new() -> Self {
19        let mut buf = vec![0u8; DHCP_MIN_LEN];
20        buf[0] = 1; // op=BOOTREQUEST
21        buf[1] = 1; // htype=Ethernet
22        buf[2] = 6; // hlen=6
23        buf[236..240].copy_from_slice(&DHCP_MAGIC_COOKIE.to_be_bytes());
24        Self { buf, options: Vec::new() }
25    }
26
27    pub fn discover() -> Self { let mut s = Self::new(); s.options.push(53); s.options.push(1); s.options.push(1); s }
28    pub fn request() -> Self { let mut s = Self::new(); s.options.push(53); s.options.push(1); s.options.push(3); s }
29    pub fn offer() -> Self { let mut s = Self::new(); s.buf[0] = 2; s.options.push(53); s.options.push(1); s.options.push(2); s }
30    pub fn ack() -> Self { let mut s = Self::new(); s.buf[0] = 2; s.options.push(53); s.options.push(1); s.options.push(5); s }
31
32    pub fn op(mut self, v: u8) -> Self { self.buf[0] = v; self }
33    pub fn htype(mut self, v: u8) -> Self { self.buf[1] = v; self }
34    pub fn hlen(mut self, v: u8) -> Self { self.buf[2] = v; self }
35    pub fn hops(mut self, v: u8) -> Self { self.buf[3] = v; self }
36    pub fn xid(mut self, v: u32) -> Self { self.buf[4..8].copy_from_slice(&v.to_be_bytes()); self }
37    pub fn secs(mut self, v: u16) -> Self { self.buf[8..10].copy_from_slice(&v.to_be_bytes()); self }
38    pub fn flags(mut self, v: u16) -> Self { self.buf[10..12].copy_from_slice(&v.to_be_bytes()); self }
39    pub fn yiaddr(mut self, ip: [u8; 4]) -> Self { self.buf[16..20].copy_from_slice(&ip); self }
40    pub fn siaddr(mut self, ip: [u8; 4]) -> Self { self.buf[20..24].copy_from_slice(&ip); self }
41    pub fn giaddr(mut self, ip: [u8; 4]) -> Self { self.buf[24..28].copy_from_slice(&ip); self }
42    pub fn chaddr(mut self, mac: &[u8; 6]) -> Self { self.buf[28..34].copy_from_slice(mac); self }
43
44    pub fn add_option(mut self, opt: &DhcpOption) -> Self {
45        match opt {
46            DhcpOption::SubnetMask(ip) => { self.options.push(1); self.options.push(4); self.options.extend_from_slice(&ip.octets()); }
47            DhcpOption::Router(ips) => { self.options.push(3); self.options.push((ips.len() * 4) as u8); for ip in ips { self.options.extend_from_slice(&ip.octets()); } }
48            DhcpOption::DnsServer(ips) => { self.options.push(6); self.options.push((ips.len() * 4) as u8); for ip in ips { self.options.extend_from_slice(&ip.octets()); } }
49            DhcpOption::HostName(s) => { self.options.push(12); self.options.push(s.len() as u8); self.options.extend_from_slice(s.as_bytes()); }
50            DhcpOption::DomainName(s) => { self.options.push(15); self.options.push(s.len() as u8); self.options.extend_from_slice(s.as_bytes()); }
51            DhcpOption::BroadcastAddr(ip) => { self.options.push(28); self.options.push(4); self.options.extend_from_slice(&ip.octets()); }
52            DhcpOption::RequestedIp(ip) => { self.options.push(50); self.options.push(4); self.options.extend_from_slice(&ip.octets()); }
53            DhcpOption::LeaseTime(t) => { self.options.push(51); self.options.push(4); self.options.extend_from_slice(&t.to_be_bytes()); }
54            DhcpOption::ServerId(ip) => { self.options.push(54); self.options.push(4); self.options.extend_from_slice(&ip.octets()); }
55            DhcpOption::ParameterList(params) => { self.options.push(55); self.options.push(params.len() as u8); self.options.extend_from_slice(params); }
56            DhcpOption::End => {}
57            _ => {}
58        }
59        self
60    }
61
62    pub fn build(mut self) -> Vec<u8> {
63        self.options.push(0xff); // End option
64        self.buf.extend_from_slice(&self.options);
65        self.buf
66    }
67}