1use alloc::vec::Vec;
9use core::convert::TryInto;
10
11use crate::{
12 error::KernelError,
13 net::{Ipv4Address, MacAddress},
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[repr(u8)]
19pub enum DhcpMessageType {
20 Discover = 1,
21 Offer = 2,
22 Request = 3,
23 Decline = 4,
24 Ack = 5,
25 Nak = 6,
26 Release = 7,
27 Inform = 8,
28}
29
30const DHCP_OP_BOOTREQUEST: u8 = 1;
32#[allow(dead_code)] const DHCP_OP_BOOTREPLY: u8 = 2;
34
35const DHCP_HTYPE_ETHERNET: u8 = 1;
37
38const DHCP_MAGIC_COOKIE: u32 = 0x63825363;
40
41const OPT_SUBNET_MASK: u8 = 1;
43const OPT_ROUTER: u8 = 3;
44const OPT_DNS_SERVER: u8 = 6;
45const OPT_REQUESTED_IP: u8 = 50;
46#[allow(dead_code)] const OPT_LEASE_TIME: u8 = 51;
48const OPT_MESSAGE_TYPE: u8 = 53;
49const OPT_SERVER_ID: u8 = 54;
50const OPT_PARAMETER_LIST: u8 = 55;
51const OPT_END: u8 = 255;
52
53#[repr(C)]
55#[derive(Debug, Clone)]
56pub struct DhcpPacket {
57 pub op: u8,
59
60 pub htype: u8,
62
63 pub hlen: u8,
65
66 pub hops: u8,
68
69 pub xid: u32,
71
72 pub secs: u16,
74
75 pub flags: u16,
77
78 pub ciaddr: Ipv4Address,
80
81 pub yiaddr: Ipv4Address,
83
84 pub siaddr: Ipv4Address,
86
87 pub giaddr: Ipv4Address,
89
90 pub chaddr: [u8; 16],
92
93 pub sname: [u8; 64],
95
96 pub file: [u8; 128],
98
99 pub options: Vec<u8>,
101}
102
103impl DhcpPacket {
104 pub fn new(message_type: DhcpMessageType, mac_address: MacAddress, xid: u32) -> Self {
106 let mut packet = Self {
107 op: DHCP_OP_BOOTREQUEST,
108 htype: DHCP_HTYPE_ETHERNET,
109 hlen: 6,
110 hops: 0,
111 xid,
112 secs: 0,
113 flags: 0x8000, ciaddr: Ipv4Address::UNSPECIFIED,
115 yiaddr: Ipv4Address::UNSPECIFIED,
116 siaddr: Ipv4Address::UNSPECIFIED,
117 giaddr: Ipv4Address::UNSPECIFIED,
118 chaddr: [0; 16],
119 sname: [0; 64],
120 file: [0; 128],
121 options: Vec::new(),
122 };
123
124 packet.chaddr[0..6].copy_from_slice(&mac_address.0);
126
127 packet.add_option_u32(DHCP_MAGIC_COOKIE);
129
130 packet.add_option_u8(OPT_MESSAGE_TYPE, message_type as u8);
132
133 packet
134 }
135
136 fn add_option_u8(&mut self, code: u8, value: u8) {
138 self.options.push(code);
139 self.options.push(1); self.options.push(value);
141 }
142
143 fn add_option_u32(&mut self, value: u32) {
145 self.options.extend_from_slice(&value.to_be_bytes());
146 }
147
148 fn add_option_ipv4(&mut self, code: u8, addr: Ipv4Address) {
150 self.options.push(code);
151 self.options.push(4); self.options.extend_from_slice(&addr.0);
153 }
154
155 pub fn add_parameter_request_list(&mut self) {
157 self.options.push(OPT_PARAMETER_LIST);
158 self.options.push(3); self.options.push(OPT_SUBNET_MASK);
160 self.options.push(OPT_ROUTER);
161 self.options.push(OPT_DNS_SERVER);
162 }
163
164 pub fn finalize(&mut self) {
166 self.options.push(OPT_END);
167 }
168
169 pub fn to_bytes(&self) -> Vec<u8> {
171 let mut bytes = Vec::with_capacity(236 + self.options.len());
172
173 bytes.push(self.op);
174 bytes.push(self.htype);
175 bytes.push(self.hlen);
176 bytes.push(self.hops);
177 bytes.extend_from_slice(&self.xid.to_be_bytes());
178 bytes.extend_from_slice(&self.secs.to_be_bytes());
179 bytes.extend_from_slice(&self.flags.to_be_bytes());
180 bytes.extend_from_slice(&self.ciaddr.0);
181 bytes.extend_from_slice(&self.yiaddr.0);
182 bytes.extend_from_slice(&self.siaddr.0);
183 bytes.extend_from_slice(&self.giaddr.0);
184 bytes.extend_from_slice(&self.chaddr);
185 bytes.extend_from_slice(&self.sname);
186 bytes.extend_from_slice(&self.file);
187 bytes.extend_from_slice(&self.options);
188
189 bytes
190 }
191
192 pub fn from_bytes(bytes: &[u8]) -> Result<Self, KernelError> {
194 if bytes.len() < 236 {
195 return Err(KernelError::InvalidArgument {
196 name: "dhcp_packet_length",
197 value: "too_short",
198 });
199 }
200
201 let mut packet = Self {
202 op: bytes[0],
203 htype: bytes[1],
204 hlen: bytes[2],
205 hops: bytes[3],
206 xid: u32::from_be_bytes(bytes[4..8].try_into().expect("DHCP xid slice")),
210 secs: u16::from_be_bytes(bytes[8..10].try_into().expect("DHCP secs slice")),
211 flags: u16::from_be_bytes(bytes[10..12].try_into().expect("DHCP flags slice")),
212 ciaddr: Ipv4Address(bytes[12..16].try_into().expect("DHCP ciaddr slice")),
213 yiaddr: Ipv4Address(bytes[16..20].try_into().expect("DHCP yiaddr slice")),
214 siaddr: Ipv4Address(bytes[20..24].try_into().expect("DHCP siaddr slice")),
215 giaddr: Ipv4Address(bytes[24..28].try_into().expect("DHCP giaddr slice")),
216 chaddr: bytes[28..44].try_into().expect("DHCP chaddr slice"),
217 sname: bytes[44..108].try_into().expect("DHCP sname slice"),
218 file: bytes[108..236].try_into().expect("DHCP file slice"),
219 options: Vec::new(),
220 };
221
222 if bytes.len() > 236 {
224 packet.options = bytes[236..].to_vec();
225 }
226
227 Ok(packet)
228 }
229
230 pub fn get_message_type(&self) -> Option<DhcpMessageType> {
232 let mut i = 4; while i < self.options.len() {
235 let code = self.options[i];
236 if code == OPT_END {
237 break;
238 }
239
240 if i + 1 >= self.options.len() {
241 break;
242 }
243
244 let len = self.options[i + 1] as usize;
245 if code == OPT_MESSAGE_TYPE && len == 1 && i + 2 < self.options.len() {
246 let msg_type = self.options[i + 2];
247 return match msg_type {
248 1 => Some(DhcpMessageType::Discover),
249 2 => Some(DhcpMessageType::Offer),
250 3 => Some(DhcpMessageType::Request),
251 4 => Some(DhcpMessageType::Decline),
252 5 => Some(DhcpMessageType::Ack),
253 6 => Some(DhcpMessageType::Nak),
254 7 => Some(DhcpMessageType::Release),
255 8 => Some(DhcpMessageType::Inform),
256 _ => None,
257 };
258 }
259
260 i += 2 + len;
261 }
262
263 None
264 }
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub enum DhcpState {
270 Init,
271 Selecting,
272 Requesting,
273 Bound,
274 Renewing,
275 Rebinding,
276}
277
278#[derive(Debug, Clone)]
280pub struct DhcpConfig {
281 pub ip_address: Ipv4Address,
282 pub subnet_mask: Ipv4Address,
283 pub router: Option<Ipv4Address>,
284 pub dns_servers: Vec<Ipv4Address>,
285 pub lease_time: u32,
286 pub server_id: Ipv4Address,
287}
288
289pub struct DhcpClient {
291 mac_address: MacAddress,
293
294 state: DhcpState,
296
297 xid: u32,
299
300 #[allow(dead_code)] config: Option<DhcpConfig>,
303}
304
305impl DhcpClient {
306 pub fn new(mac_address: MacAddress) -> Self {
308 Self {
309 mac_address,
310 state: DhcpState::Init,
311 xid: 0x12345678, config: None,
313 }
314 }
315
316 pub fn create_discover(&self) -> DhcpPacket {
318 let mut packet = DhcpPacket::new(DhcpMessageType::Discover, self.mac_address, self.xid);
319 packet.add_parameter_request_list();
320 packet.finalize();
321 packet
322 }
323
324 pub fn create_request(&self, offered_ip: Ipv4Address, server_id: Ipv4Address) -> DhcpPacket {
326 let mut packet = DhcpPacket::new(DhcpMessageType::Request, self.mac_address, self.xid);
327 packet.add_option_ipv4(OPT_REQUESTED_IP, offered_ip);
328 packet.add_option_ipv4(OPT_SERVER_ID, server_id);
329 packet.add_parameter_request_list();
330 packet.finalize();
331 packet
332 }
333
334 pub fn process_offer(&mut self, packet: &DhcpPacket) -> Result<(), KernelError> {
336 if self.state != DhcpState::Selecting {
337 return Err(KernelError::InvalidState {
338 expected: "Selecting",
339 actual: "Other",
340 });
341 }
342
343 let options = parse_dhcp_options(&packet.options);
344 let offered_ip = packet.yiaddr;
345 let server_id = options.server_id.unwrap_or(packet.siaddr);
346
347 println!(
348 "[DHCP] Received OFFER: {}.{}.{}.{} from server {}.{}.{}.{}",
349 offered_ip.0[0],
350 offered_ip.0[1],
351 offered_ip.0[2],
352 offered_ip.0[3],
353 server_id.0[0],
354 server_id.0[1],
355 server_id.0[2],
356 server_id.0[3],
357 );
358
359 let request = self.create_request(offered_ip, server_id);
361 let request_bytes = request.to_bytes();
362 send_dhcp_packet(&request_bytes);
363
364 self.state = DhcpState::Requesting;
365 Ok(())
366 }
367
368 pub fn process_ack(&mut self, packet: &DhcpPacket) -> Result<(), KernelError> {
371 if self.state != DhcpState::Requesting {
372 return Err(KernelError::InvalidState {
373 expected: "Requesting",
374 actual: "Other",
375 });
376 }
377
378 let options = parse_dhcp_options(&packet.options);
379
380 let ip = packet.yiaddr;
381 let subnet = options
382 .subnet_mask
383 .unwrap_or(Ipv4Address::new(255, 255, 255, 0));
384 let gateway = options.router;
385 let lease = options.lease_time.unwrap_or(3600);
386
387 let config = DhcpConfig {
388 ip_address: ip,
389 subnet_mask: subnet,
390 router: gateway,
391 dns_servers: options.dns_servers,
392 lease_time: lease,
393 server_id: options.server_id.unwrap_or(packet.siaddr),
394 };
395
396 println!(
397 "[DHCP] ACK: IP {}.{}.{}.{} mask {}.{}.{}.{} lease {}s",
398 ip.0[0],
399 ip.0[1],
400 ip.0[2],
401 ip.0[3],
402 subnet.0[0],
403 subnet.0[1],
404 subnet.0[2],
405 subnet.0[3],
406 lease,
407 );
408 if let Some(gw) = gateway {
409 println!(
410 "[DHCP] Gateway: {}.{}.{}.{}",
411 gw.0[0], gw.0[1], gw.0[2], gw.0[3]
412 );
413 }
414
415 super::ip::set_interface_config(ip, subnet, gateway);
417
418 if let Some(gw) = gateway {
420 super::ip::add_route(super::ip::RouteEntry {
421 destination: Ipv4Address::new(0, 0, 0, 0),
422 netmask: Ipv4Address::new(0, 0, 0, 0),
423 gateway: Some(gw),
424 interface: 0,
425 });
426 }
427
428 self.config = Some(config);
429 self.state = DhcpState::Bound;
430
431 Ok(())
432 }
433
434 pub fn process_response(&mut self, data: &[u8]) -> Result<(), KernelError> {
439 let packet = DhcpPacket::from_bytes(data)?;
440
441 if packet.xid != self.xid {
443 return Ok(()); }
445
446 match packet.get_message_type() {
447 Some(DhcpMessageType::Offer) => self.process_offer(&packet),
448 Some(DhcpMessageType::Ack) => self.process_ack(&packet),
449 Some(DhcpMessageType::Nak) => {
450 println!("[DHCP] Received NAK, restarting negotiation");
451 self.state = DhcpState::Init;
452 Ok(())
453 }
454 _ => Ok(()),
455 }
456 }
457
458 pub fn state(&self) -> DhcpState {
460 self.state
461 }
462
463 pub fn config(&self) -> Option<&DhcpConfig> {
465 self.config.as_ref()
466 }
467
468 pub fn start(&mut self) -> Result<(), KernelError> {
470 println!("[DHCP] Starting DHCP negotiation");
471
472 let discover = self.create_discover();
473 let discover_bytes = discover.to_bytes();
474
475 println!("[DHCP] Sending DISCOVER ({} bytes)", discover_bytes.len());
476 send_dhcp_packet(&discover_bytes);
477
478 self.state = DhcpState::Selecting;
479 Ok(())
480 }
481}
482
483#[derive(Debug, Default)]
485struct ParsedDhcpOptions {
486 subnet_mask: Option<Ipv4Address>,
487 router: Option<Ipv4Address>,
488 dns_servers: Vec<Ipv4Address>,
489 lease_time: Option<u32>,
490 server_id: Option<Ipv4Address>,
491}
492
493fn parse_dhcp_options(options: &[u8]) -> ParsedDhcpOptions {
495 let mut result = ParsedDhcpOptions::default();
496 let mut i = 4; while i < options.len() {
499 let code = options[i];
500 if code == OPT_END {
501 break;
502 }
503 if code == 0 {
504 i += 1;
506 continue;
507 }
508 if i + 1 >= options.len() {
509 break;
510 }
511 let len = options[i + 1] as usize;
512 if i + 2 + len > options.len() {
513 break;
514 }
515 let data = &options[i + 2..i + 2 + len];
516
517 match code {
518 OPT_SUBNET_MASK if len == 4 => {
519 result.subnet_mask = Some(Ipv4Address([data[0], data[1], data[2], data[3]]));
520 }
521 OPT_ROUTER if len >= 4 => {
522 result.router = Some(Ipv4Address([data[0], data[1], data[2], data[3]]));
523 }
524 OPT_DNS_SERVER if len >= 4 => {
525 for chunk in data.chunks_exact(4) {
526 result
527 .dns_servers
528 .push(Ipv4Address([chunk[0], chunk[1], chunk[2], chunk[3]]));
529 }
530 }
531 OPT_LEASE_TIME if len == 4 => {
532 result.lease_time = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]));
533 }
534 OPT_SERVER_ID if len == 4 => {
535 result.server_id = Some(Ipv4Address([data[0], data[1], data[2], data[3]]));
536 }
537 _ => {} }
539
540 i += 2 + len;
541 }
542
543 result
544}
545
546fn send_dhcp_packet(data: &[u8]) {
548 let src = super::SocketAddr::v4(Ipv4Address::ANY, 68);
549 let dst = super::SocketAddr::v4(Ipv4Address::BROADCAST, 67);
550 let _ = super::udp::send_packet(src, dst, data);
551}
552
553static DHCP_CLIENT: spin::Mutex<Option<DhcpClient>> = spin::Mutex::new(None);
555
556pub fn start_dhcp() -> Result<(), KernelError> {
558 let mac =
559 super::device::with_device("eth0", |dev| dev.mac_address()).unwrap_or(MacAddress::ZERO);
560
561 let mut lock = DHCP_CLIENT.lock();
562 let client = lock.get_or_insert_with(|| DhcpClient::new(mac));
563 client.start()
564}
565
566pub fn get_dhcp_state() -> Option<DhcpState> {
568 let lock = DHCP_CLIENT.lock();
569 lock.as_ref().map(|c| c.state())
570}
571
572pub fn init() -> Result<(), KernelError> {
574 println!("[DHCP] DHCP client initialized");
575 Ok(())
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581
582 #[test]
583 fn test_dhcp_packet_creation() {
584 let mac = MacAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]);
585 let packet = DhcpPacket::new(DhcpMessageType::Discover, mac, 0x12345678);
586
587 assert_eq!(packet.op, DHCP_OP_BOOTREQUEST);
588 assert_eq!(packet.htype, DHCP_HTYPE_ETHERNET);
589 assert_eq!(packet.hlen, 6);
590 }
591
592 #[test]
593 fn test_dhcp_serialization() {
594 let mac = MacAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]);
595 let mut packet = DhcpPacket::new(DhcpMessageType::Discover, mac, 0x12345678);
596 packet.finalize();
597
598 let bytes = packet.to_bytes();
599 assert!(bytes.len() >= 236);
600 }
601}