stackforge_core/layer/dhcp/
builder.rs1use std::net::Ipv4Addr;
2
3use crate::layer::dhcp::options::{DhcpOption, code, msg_type, serialize_options};
4use crate::layer::field::MacAddress;
5
6const MAGIC_COOKIE: [u8; 4] = [99, 130, 83, 99];
8
9const BOOTREQUEST: u8 = 1;
11const BOOTREPLY: u8 = 2;
12
13pub struct DhcpBuilder {
15 op: u8,
16 htype: u8,
17 hlen: u8,
18 hops: u8,
19 xid: u32,
20 secs: u16,
21 flags: u16,
22 ciaddr: Ipv4Addr,
23 yiaddr: Ipv4Addr,
24 siaddr: Ipv4Addr,
25 giaddr: Ipv4Addr,
26 chaddr: [u8; 16],
27 sname: [u8; 64],
28 file: [u8; 128],
29 options: Vec<DhcpOption>,
30}
31
32impl Default for DhcpBuilder {
33 fn default() -> Self {
34 Self {
35 op: BOOTREQUEST,
36 htype: 1, hlen: 6,
38 hops: 0,
39 xid: 0,
40 secs: 0,
41 flags: 0,
42 ciaddr: Ipv4Addr::UNSPECIFIED,
43 yiaddr: Ipv4Addr::UNSPECIFIED,
44 siaddr: Ipv4Addr::UNSPECIFIED,
45 giaddr: Ipv4Addr::UNSPECIFIED,
46 chaddr: [0u8; 16],
47 sname: [0u8; 64],
48 file: [0u8; 128],
49 options: Vec::new(),
50 }
51 }
52}
53
54impl DhcpBuilder {
55 #[must_use]
56 pub fn new() -> Self {
57 Self::default()
58 }
59
60 #[must_use]
62 pub fn discover(client_mac: MacAddress, xid: u32) -> Self {
63 let mut b = Self::new()
64 .op(BOOTREQUEST)
65 .xid(xid)
66 .chaddr_mac(client_mac)
67 .flags(0x8000); b.options.push(DhcpOption::message_type(msg_type::DISCOVER));
69 b
70 }
71
72 #[must_use]
74 pub fn offer(
75 xid: u32,
76 client_mac: MacAddress,
77 offered_ip: Ipv4Addr,
78 server_ip: Ipv4Addr,
79 ) -> Self {
80 let mut b = Self::new()
81 .op(BOOTREPLY)
82 .xid(xid)
83 .yiaddr(offered_ip)
84 .siaddr(server_ip)
85 .chaddr_mac(client_mac);
86 b.options.push(DhcpOption::message_type(msg_type::OFFER));
87 b.options.push(DhcpOption::server_id(server_ip));
88 b
89 }
90
91 #[must_use]
93 pub fn request(
94 client_mac: MacAddress,
95 xid: u32,
96 requested_ip: Ipv4Addr,
97 server_ip: Ipv4Addr,
98 ) -> Self {
99 let mut b = Self::new()
100 .op(BOOTREQUEST)
101 .xid(xid)
102 .chaddr_mac(client_mac)
103 .flags(0x8000);
104 b.options.push(DhcpOption::message_type(msg_type::REQUEST));
105 b.options.push(DhcpOption::new(
106 code::REQUESTED_IP,
107 requested_ip.octets().to_vec(),
108 ));
109 b.options.push(DhcpOption::server_id(server_ip));
110 b
111 }
112
113 #[must_use]
115 pub fn ack(
116 xid: u32,
117 client_mac: MacAddress,
118 assigned_ip: Ipv4Addr,
119 server_ip: Ipv4Addr,
120 ) -> Self {
121 let mut b = Self::new()
122 .op(BOOTREPLY)
123 .xid(xid)
124 .yiaddr(assigned_ip)
125 .siaddr(server_ip)
126 .chaddr_mac(client_mac);
127 b.options.push(DhcpOption::message_type(msg_type::ACK));
128 b.options.push(DhcpOption::server_id(server_ip));
129 b
130 }
131
132 #[must_use]
134 pub fn nak(xid: u32, client_mac: MacAddress, server_ip: Ipv4Addr) -> Self {
135 let mut b = Self::new().op(BOOTREPLY).xid(xid).chaddr_mac(client_mac);
136 b.options.push(DhcpOption::message_type(msg_type::NAK));
137 b.options.push(DhcpOption::server_id(server_ip));
138 b
139 }
140
141 #[must_use]
142 pub fn op(mut self, op: u8) -> Self {
143 self.op = op;
144 self
145 }
146
147 #[must_use]
148 pub fn xid(mut self, xid: u32) -> Self {
149 self.xid = xid;
150 self
151 }
152
153 #[must_use]
154 pub fn flags(mut self, flags: u16) -> Self {
155 self.flags = flags;
156 self
157 }
158
159 #[must_use]
160 pub fn ciaddr(mut self, ip: Ipv4Addr) -> Self {
161 self.ciaddr = ip;
162 self
163 }
164
165 #[must_use]
166 pub fn yiaddr(mut self, ip: Ipv4Addr) -> Self {
167 self.yiaddr = ip;
168 self
169 }
170
171 #[must_use]
172 pub fn siaddr(mut self, ip: Ipv4Addr) -> Self {
173 self.siaddr = ip;
174 self
175 }
176
177 #[must_use]
178 pub fn giaddr(mut self, ip: Ipv4Addr) -> Self {
179 self.giaddr = ip;
180 self
181 }
182
183 #[must_use]
184 pub fn chaddr_mac(mut self, mac: MacAddress) -> Self {
185 self.chaddr[0..6].copy_from_slice(&mac.0);
186 self
187 }
188
189 #[must_use]
191 pub fn option(mut self, opt: DhcpOption) -> Self {
192 self.options.push(opt);
193 self
194 }
195
196 #[must_use]
198 pub fn lease_time(self, seconds: u32) -> Self {
199 self.option(DhcpOption::lease_time(seconds))
200 }
201
202 #[must_use]
204 pub fn subnet_mask(self, mask: Ipv4Addr) -> Self {
205 self.option(DhcpOption::subnet_mask(mask))
206 }
207
208 #[must_use]
210 pub fn router(self, ip: Ipv4Addr) -> Self {
211 self.option(DhcpOption::router(ip))
212 }
213
214 #[must_use]
216 pub fn dns(self, servers: &[Ipv4Addr]) -> Self {
217 self.option(DhcpOption::dns(servers))
218 }
219
220 #[must_use]
222 pub fn domain_name(self, name: &str) -> Self {
223 self.option(DhcpOption::domain_name(name))
224 }
225
226 #[must_use]
231 pub fn build(&self) -> Vec<u8> {
232 let opts_bytes = serialize_options(&self.options);
233 let mut out = Vec::with_capacity(240 + opts_bytes.len());
234
235 out.push(self.op);
237 out.push(self.htype);
238 out.push(self.hlen);
239 out.push(self.hops);
240 out.extend_from_slice(&self.xid.to_be_bytes());
241 out.extend_from_slice(&self.secs.to_be_bytes());
242 out.extend_from_slice(&self.flags.to_be_bytes());
243 out.extend_from_slice(&self.ciaddr.octets());
244 out.extend_from_slice(&self.yiaddr.octets());
245 out.extend_from_slice(&self.siaddr.octets());
246 out.extend_from_slice(&self.giaddr.octets());
247 out.extend_from_slice(&self.chaddr);
248 out.extend_from_slice(&self.sname);
249 out.extend_from_slice(&self.file);
250
251 out.extend_from_slice(&MAGIC_COOKIE);
253
254 out.extend_from_slice(&opts_bytes);
256
257 out
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_discover_build() {
267 let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
268 let pkt = DhcpBuilder::discover(mac, 0x12345678).build();
269
270 assert_eq!(pkt[0], BOOTREQUEST); assert_eq!(pkt[1], 1); assert_eq!(pkt[2], 6); assert_eq!(&pkt[4..8], &[0x12, 0x34, 0x56, 0x78]);
275 assert_eq!(&pkt[10..12], &[0x80, 0x00]);
277 assert_eq!(&pkt[28..34], &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
279 assert_eq!(&pkt[236..240], &[99, 130, 83, 99]);
281 assert_eq!(pkt[240], code::MESSAGE_TYPE);
283 assert_eq!(pkt[241], 1); assert_eq!(pkt[242], msg_type::DISCOVER);
285 }
286
287 #[test]
288 fn test_offer_build() {
289 let mac = MacAddress::new([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
290 let pkt = DhcpBuilder::offer(
291 0xaabbccdd,
292 mac,
293 Ipv4Addr::new(192, 168, 1, 100),
294 Ipv4Addr::new(192, 168, 1, 1),
295 )
296 .lease_time(3600)
297 .subnet_mask(Ipv4Addr::new(255, 255, 255, 0))
298 .build();
299
300 assert_eq!(pkt[0], BOOTREPLY);
301 assert_eq!(&pkt[16..20], &[192, 168, 1, 100]);
303 assert_eq!(&pkt[20..24], &[192, 168, 1, 1]);
305 }
306
307 #[test]
308 fn test_ack_build() {
309 let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
310 let pkt = DhcpBuilder::ack(
311 0x11223344,
312 mac,
313 Ipv4Addr::new(10, 0, 0, 50),
314 Ipv4Addr::new(10, 0, 0, 1),
315 )
316 .build();
317
318 assert_eq!(pkt[0], BOOTREPLY);
319 assert_eq!(&pkt[16..20], &[10, 0, 0, 50]); }
321}