1#![allow(dead_code)] use alloc::vec::Vec;
10
11use super::Ipv6Address;
12use crate::error::KernelError;
13
14pub const ICMPV6_DEST_UNREACHABLE: u8 = 1;
21pub const ICMPV6_PACKET_TOO_BIG: u8 = 2;
23pub const ICMPV6_TIME_EXCEEDED: u8 = 3;
25pub const ICMPV6_PARAMETER_PROBLEM: u8 = 4;
27
28pub const ICMPV6_ECHO_REQUEST: u8 = 128;
31pub const ICMPV6_ECHO_REPLY: u8 = 129;
33
34pub const ICMPV6_ROUTER_SOLICIT: u8 = 133;
37pub const ICMPV6_ROUTER_ADVERT: u8 = 134;
39pub const ICMPV6_NEIGHBOR_SOLICIT: u8 = 135;
41pub const ICMPV6_NEIGHBOR_ADVERT: u8 = 136;
43
44pub const ICMPV6_NO_ROUTE: u8 = 0;
47pub const ICMPV6_ADMIN_PROHIBITED: u8 = 1;
49pub const ICMPV6_BEYOND_SCOPE: u8 = 2;
51pub const ICMPV6_ADDR_UNREACHABLE: u8 = 3;
53pub const ICMPV6_PORT_UNREACHABLE: u8 = 4;
55
56pub const ICMPV6_HOP_LIMIT_EXCEEDED: u8 = 0;
59pub const ICMPV6_FRAGMENT_REASSEMBLY_EXCEEDED: u8 = 1;
61
62pub const ICMPV6_HEADER_SIZE: usize = 4;
64
65pub const ICMPV6_ECHO_HEADER_SIZE: usize = 8;
67
68#[derive(Debug, Clone, Copy)]
74#[repr(C)]
75pub struct Icmpv6Header {
76 pub icmp_type: u8,
78 pub code: u8,
80 pub checksum: u16,
82}
83
84impl Icmpv6Header {
85 pub fn from_bytes(data: &[u8]) -> Result<Self, KernelError> {
87 if data.len() < ICMPV6_HEADER_SIZE {
88 return Err(KernelError::InvalidArgument {
89 name: "icmpv6_header",
90 value: "too_short",
91 });
92 }
93
94 Ok(Self {
95 icmp_type: data[0],
96 code: data[1],
97 checksum: u16::from_be_bytes([data[2], data[3]]),
98 })
99 }
100
101 pub fn to_bytes(&self) -> [u8; ICMPV6_HEADER_SIZE] {
103 let mut bytes = [0u8; ICMPV6_HEADER_SIZE];
104 bytes[0] = self.icmp_type;
105 bytes[1] = self.code;
106 bytes[2..4].copy_from_slice(&self.checksum.to_be_bytes());
107 bytes
108 }
109}
110
111pub fn handle_icmpv6(
124 src: &Ipv6Address,
125 dst: &Ipv6Address,
126 data: &[u8],
127) -> Result<Option<Vec<u8>>, KernelError> {
128 if data.len() < ICMPV6_HEADER_SIZE {
129 return Err(KernelError::InvalidArgument {
130 name: "icmpv6_packet",
131 value: "too_short",
132 });
133 }
134
135 let header = Icmpv6Header::from_bytes(data)?;
136
137 if !verify_checksum(src, dst, data) {
139 return Err(KernelError::InvalidArgument {
140 name: "icmpv6_checksum",
141 value: "invalid",
142 });
143 }
144
145 match header.icmp_type {
146 ICMPV6_ECHO_REQUEST => handle_echo_request(src, dst, data),
147 ICMPV6_ECHO_REPLY => {
148 handle_echo_reply(src, data);
149 Ok(None)
150 }
151 ICMPV6_DEST_UNREACHABLE => {
152 handle_dest_unreachable(src, header.code, data);
153 Ok(None)
154 }
155 ICMPV6_PACKET_TOO_BIG => {
156 handle_packet_too_big(src, data);
157 Ok(None)
158 }
159 ICMPV6_TIME_EXCEEDED => {
160 handle_time_exceeded(src, header.code, data);
161 Ok(None)
162 }
163 ICMPV6_ROUTER_SOLICIT
165 | ICMPV6_ROUTER_ADVERT
166 | ICMPV6_NEIGHBOR_SOLICIT
167 | ICMPV6_NEIGHBOR_ADVERT => {
168 if let Some(reply_icmpv6) = super::ipv6::handle_ndp(src, dst, data)? {
169 let reply_src =
171 super::ipv6::select_source_address(src).unwrap_or(Ipv6Address::UNSPECIFIED);
172 let reply_packet = super::ipv6::build_ipv6(
173 &reply_src,
174 src,
175 super::ipv6::NEXT_HEADER_ICMPV6,
176 &reply_icmpv6,
177 );
178 let _ = super::ipv6::send(
180 &reply_src,
181 src,
182 super::ipv6::NEXT_HEADER_ICMPV6,
183 &reply_icmpv6,
184 );
185 let _ = reply_packet;
187 Ok(None)
188 } else {
189 Ok(None)
190 }
191 }
192 _ => {
193 Ok(None)
195 }
196 }
197}
198
199fn handle_echo_request(
201 src: &Ipv6Address,
202 dst: &Ipv6Address,
203 data: &[u8],
204) -> Result<Option<Vec<u8>>, KernelError> {
205 if data.len() < ICMPV6_ECHO_HEADER_SIZE {
206 return Err(KernelError::InvalidArgument {
207 name: "icmpv6_echo",
208 value: "too_short",
209 });
210 }
211
212 let id = u16::from_be_bytes([data[4], data[5]]);
214 let seq = u16::from_be_bytes([data[6], data[7]]);
215 let echo_data = &data[ICMPV6_ECHO_HEADER_SIZE..];
216
217 let reply_src = super::ipv6::select_source_address(src).unwrap_or(*dst);
219
220 let reply = build_echo_reply(&reply_src, src, id, seq, echo_data);
222
223 let _ = super::ipv6::send(&reply_src, src, super::ipv6::NEXT_HEADER_ICMPV6, &reply);
225
226 Ok(None)
227}
228
229fn handle_echo_reply(src: &Ipv6Address, data: &[u8]) {
231 if data.len() >= ICMPV6_ECHO_HEADER_SIZE {
232 let id = u16::from_be_bytes([data[4], data[5]]);
233 let seq = u16::from_be_bytes([data[6], data[7]]);
234 let payload_len = data.len() - ICMPV6_ECHO_HEADER_SIZE;
235
236 println!(
237 "[ICMPv6] Echo reply from {}: id={} seq={} len={}",
238 super::ipv6::format_ipv6_compressed(src),
239 id,
240 seq,
241 payload_len,
242 );
243
244 LAST_ECHO_REPLY.store(seq as u64, core::sync::atomic::Ordering::Relaxed);
246 }
247}
248
249fn handle_dest_unreachable(src: &Ipv6Address, code: u8, _data: &[u8]) {
251 let reason = match code {
252 ICMPV6_NO_ROUTE => "no route to destination",
253 ICMPV6_ADMIN_PROHIBITED => "administratively prohibited",
254 ICMPV6_BEYOND_SCOPE => "beyond scope",
255 ICMPV6_ADDR_UNREACHABLE => "address unreachable",
256 ICMPV6_PORT_UNREACHABLE => "port unreachable",
257 _ => "unknown",
258 };
259 println!(
260 "[ICMPv6] Destination unreachable from {}: {} (code {})",
261 super::ipv6::format_ipv6_compressed(src),
262 reason,
263 code,
264 );
265}
266
267fn handle_packet_too_big(src: &Ipv6Address, data: &[u8]) {
269 if data.len() >= 8 {
270 let mtu = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
271 println!(
272 "[ICMPv6] Packet too big from {}: MTU={}",
273 super::ipv6::format_ipv6_compressed(src),
274 mtu,
275 );
276 }
277}
278
279fn handle_time_exceeded(src: &Ipv6Address, code: u8, _data: &[u8]) {
281 let reason = match code {
282 ICMPV6_HOP_LIMIT_EXCEEDED => "hop limit exceeded",
283 ICMPV6_FRAGMENT_REASSEMBLY_EXCEEDED => "fragment reassembly time exceeded",
284 _ => "unknown",
285 };
286 println!(
287 "[ICMPv6] Time exceeded from {}: {} (code {})",
288 super::ipv6::format_ipv6_compressed(src),
289 reason,
290 code,
291 );
292}
293
294pub fn build_echo_reply(
302 src: &Ipv6Address,
303 dst: &Ipv6Address,
304 id: u16,
305 seq: u16,
306 data: &[u8],
307) -> Vec<u8> {
308 let mut msg = Vec::with_capacity(ICMPV6_ECHO_HEADER_SIZE + data.len());
309
310 msg.push(ICMPV6_ECHO_REPLY); msg.push(0); msg.extend_from_slice(&[0u8; 2]); msg.extend_from_slice(&id.to_be_bytes());
317 msg.extend_from_slice(&seq.to_be_bytes());
318
319 msg.extend_from_slice(data);
321
322 let checksum = super::ipv6::compute_icmpv6_checksum(&src.0, &dst.0, &msg);
324 msg[2] = (checksum >> 8) as u8;
325 msg[3] = (checksum & 0xff) as u8;
326
327 msg
328}
329
330pub fn build_echo_request(
334 src: &Ipv6Address,
335 dst: &Ipv6Address,
336 id: u16,
337 seq: u16,
338 data: &[u8],
339) -> Vec<u8> {
340 let mut msg = Vec::with_capacity(ICMPV6_ECHO_HEADER_SIZE + data.len());
341
342 msg.push(ICMPV6_ECHO_REQUEST); msg.push(0); msg.extend_from_slice(&[0u8; 2]); msg.extend_from_slice(&id.to_be_bytes());
349 msg.extend_from_slice(&seq.to_be_bytes());
350
351 msg.extend_from_slice(data);
353
354 let checksum = super::ipv6::compute_icmpv6_checksum(&src.0, &dst.0, &msg);
356 msg[2] = (checksum >> 8) as u8;
357 msg[3] = (checksum & 0xff) as u8;
358
359 msg
360}
361
362pub fn build_dest_unreachable(
367 src: &Ipv6Address,
368 dst: &Ipv6Address,
369 code: u8,
370 invoking_packet: &[u8],
371) -> Vec<u8> {
372 let max_payload = super::ipv6::IPV6_MIN_MTU - super::ipv6::IPV6_HEADER_SIZE - 8;
374 let payload_len = invoking_packet.len().min(max_payload);
375
376 let mut msg = Vec::with_capacity(8 + payload_len);
377
378 msg.push(ICMPV6_DEST_UNREACHABLE); msg.push(code); msg.extend_from_slice(&[0u8; 2]); msg.extend_from_slice(&[0u8; 4]); msg.extend_from_slice(&invoking_packet[..payload_len]);
383
384 let checksum = super::ipv6::compute_icmpv6_checksum(&src.0, &dst.0, &msg);
386 msg[2] = (checksum >> 8) as u8;
387 msg[3] = (checksum & 0xff) as u8;
388
389 msg
390}
391
392pub fn build_packet_too_big(
394 src: &Ipv6Address,
395 dst: &Ipv6Address,
396 mtu: u32,
397 invoking_packet: &[u8],
398) -> Vec<u8> {
399 let max_payload = super::ipv6::IPV6_MIN_MTU - super::ipv6::IPV6_HEADER_SIZE - 8;
400 let payload_len = invoking_packet.len().min(max_payload);
401
402 let mut msg = Vec::with_capacity(8 + payload_len);
403
404 msg.push(ICMPV6_PACKET_TOO_BIG); msg.push(0); msg.extend_from_slice(&[0u8; 2]); msg.extend_from_slice(&mtu.to_be_bytes()); msg.extend_from_slice(&invoking_packet[..payload_len]);
409
410 let checksum = super::ipv6::compute_icmpv6_checksum(&src.0, &dst.0, &msg);
412 msg[2] = (checksum >> 8) as u8;
413 msg[3] = (checksum & 0xff) as u8;
414
415 msg
416}
417
418pub fn build_time_exceeded(
420 src: &Ipv6Address,
421 dst: &Ipv6Address,
422 code: u8,
423 invoking_packet: &[u8],
424) -> Vec<u8> {
425 let max_payload = super::ipv6::IPV6_MIN_MTU - super::ipv6::IPV6_HEADER_SIZE - 8;
426 let payload_len = invoking_packet.len().min(max_payload);
427
428 let mut msg = Vec::with_capacity(8 + payload_len);
429
430 msg.push(ICMPV6_TIME_EXCEEDED); msg.push(code); msg.extend_from_slice(&[0u8; 2]); msg.extend_from_slice(&[0u8; 4]); msg.extend_from_slice(&invoking_packet[..payload_len]);
435
436 let checksum = super::ipv6::compute_icmpv6_checksum(&src.0, &dst.0, &msg);
438 msg[2] = (checksum >> 8) as u8;
439 msg[3] = (checksum & 0xff) as u8;
440
441 msg
442}
443
444fn verify_checksum(src: &Ipv6Address, dst: &Ipv6Address, data: &[u8]) -> bool {
453 if data.len() < ICMPV6_HEADER_SIZE {
454 return false;
455 }
456
457 let computed = super::ipv6::compute_icmpv6_checksum(&src.0, &dst.0, data);
460
461 computed == 0
463}
464
465pub fn compute_icmpv6_checksum(src: &[u8; 16], dst: &[u8; 16], data: &[u8]) -> u16 {
469 super::ipv6::compute_icmpv6_checksum(src, dst, data)
470}
471
472static LAST_ECHO_REPLY: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
478
479pub fn get_last_echo_reply_seq() -> u64 {
481 LAST_ECHO_REPLY.load(core::sync::atomic::Ordering::Relaxed)
482}
483
484pub fn reset_echo_reply_tracker() {
486 LAST_ECHO_REPLY.store(0, core::sync::atomic::Ordering::Relaxed);
487}
488
489#[derive(Debug, Clone, Copy, Default)]
495pub struct Icmpv6Stats {
496 pub echo_requests_received: u64,
498 pub echo_replies_sent: u64,
500 pub echo_replies_received: u64,
502 pub errors_received: u64,
504 pub ndp_messages: u64,
506}
507
508pub fn init() -> Result<(), KernelError> {
514 println!("[ICMPv6] Initializing ICMPv6...");
515 println!("[ICMPv6] ICMPv6 initialized");
516 Ok(())
517}
518
519#[cfg(test)]
524mod tests {
525 use super::*;
526
527 #[test]
528 fn test_icmpv6_header_parse() {
529 let data = [128u8, 0, 0x12, 0x34]; let header = Icmpv6Header::from_bytes(&data).unwrap();
531 assert_eq!(header.icmp_type, ICMPV6_ECHO_REQUEST);
532 assert_eq!(header.code, 0);
533 assert_eq!(header.checksum, 0x1234);
534 }
535
536 #[test]
537 fn test_icmpv6_header_roundtrip() {
538 let header = Icmpv6Header {
539 icmp_type: ICMPV6_ECHO_REPLY,
540 code: 0,
541 checksum: 0xABCD,
542 };
543 let bytes = header.to_bytes();
544 let parsed = Icmpv6Header::from_bytes(&bytes).unwrap();
545 assert_eq!(parsed.icmp_type, header.icmp_type);
546 assert_eq!(parsed.code, header.code);
547 assert_eq!(parsed.checksum, header.checksum);
548 }
549
550 #[test]
551 fn test_icmpv6_header_too_short() {
552 let data = [128u8, 0];
553 assert!(Icmpv6Header::from_bytes(&data).is_err());
554 }
555
556 #[test]
557 fn test_build_echo_request() {
558 let src = Ipv6Address::LOCALHOST;
559 let dst = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
560 let data = b"ping6";
561 let msg = build_echo_request(&src, &dst, 1, 1, data);
562
563 assert_eq!(msg[0], ICMPV6_ECHO_REQUEST);
564 assert_eq!(msg[1], 0); assert_eq!(u16::from_be_bytes([msg[4], msg[5]]), 1);
567 assert_eq!(u16::from_be_bytes([msg[6], msg[7]]), 1);
569 assert_eq!(&msg[8..], data);
571 let checksum = u16::from_be_bytes([msg[2], msg[3]]);
573 assert_ne!(checksum, 0);
574 }
575
576 #[test]
577 fn test_build_echo_reply() {
578 let src = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
579 let dst = Ipv6Address::LOCALHOST;
580 let data = b"pong6";
581 let msg = build_echo_reply(&src, &dst, 42, 7, data);
582
583 assert_eq!(msg[0], ICMPV6_ECHO_REPLY);
584 assert_eq!(u16::from_be_bytes([msg[4], msg[5]]), 42);
585 assert_eq!(u16::from_be_bytes([msg[6], msg[7]]), 7);
586 assert_eq!(&msg[8..], data);
587 }
588
589 #[test]
590 fn test_build_dest_unreachable() {
591 let src = Ipv6Address::LOCALHOST;
592 let dst = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
593 let invoking = [0u8; 64];
594 let msg = build_dest_unreachable(&src, &dst, ICMPV6_PORT_UNREACHABLE, &invoking);
595
596 assert_eq!(msg[0], ICMPV6_DEST_UNREACHABLE);
597 assert_eq!(msg[1], ICMPV6_PORT_UNREACHABLE);
598 assert!(msg.len() >= 8 + 64);
599 }
600
601 #[test]
602 fn test_build_packet_too_big() {
603 let src = Ipv6Address::LOCALHOST;
604 let dst = Ipv6Address::LOCALHOST;
605 let invoking = [0u8; 32];
606 let msg = build_packet_too_big(&src, &dst, 1280, &invoking);
607
608 assert_eq!(msg[0], ICMPV6_PACKET_TOO_BIG);
609 assert_eq!(msg[1], 0);
610 let mtu = u32::from_be_bytes([msg[4], msg[5], msg[6], msg[7]]);
611 assert_eq!(mtu, 1280);
612 }
613
614 #[test]
615 fn test_echo_reply_tracker() {
616 reset_echo_reply_tracker();
617 assert_eq!(get_last_echo_reply_seq(), 0);
618
619 LAST_ECHO_REPLY.store(42, core::sync::atomic::Ordering::Relaxed);
620 assert_eq!(get_last_echo_reply_seq(), 42);
621
622 reset_echo_reply_tracker();
623 assert_eq!(get_last_echo_reply_seq(), 0);
624 }
625}