1use alloc::vec;
4use alloc::vec::Vec;
5use core::net::Ipv6Addr;
6
7use crate::types::IpProtocol;
8use crate::util::{internet_checksum, pseudo_header_checksum, read_u16be, read_u32be, write_u16be};
9
10pub const ICMP_HEADER_LEN: usize = 8;
15
16pub const ICMP_TYPE_ECHO_REPLY: u8 = 0;
17pub const ICMP_TYPE_DEST_UNREACH: u8 = 3;
18pub const ICMP_TYPE_ECHO_REQUEST: u8 = 8;
19pub const ICMP_TYPE_TIME_EXCEEDED: u8 = 11;
20
21pub const ICMPV6_HEADER_LEN: usize = 8;
26
27pub const ICMPV6_TYPE_ECHO_REQUEST: u8 = 128;
28pub const ICMPV6_TYPE_ECHO_REPLY: u8 = 129;
29pub const ICMPV6_TYPE_ROUTER_SOLICIT: u8 = 133;
30pub const ICMPV6_TYPE_ROUTER_ADVERT: u8 = 134;
31pub const ICMPV6_TYPE_NEIGHBOR_SOLICIT: u8 = 135;
32pub const ICMPV6_TYPE_NEIGHBOR_ADVERT: u8 = 136;
33
34#[derive(Debug, Clone)]
40pub enum IcmpMessage<'a> {
41 EchoReply {
42 identifier: u16,
43 sequence_number: u16,
44 data: &'a [u8],
45 },
46 EchoRequest {
47 identifier: u16,
48 sequence_number: u16,
49 data: &'a [u8],
50 },
51 DestUnreachable {
52 code: u8,
53 data: &'a [u8],
54 },
55 TimeExceeded {
56 code: u8,
57 data: &'a [u8],
58 },
59 Unknown {
60 type_: u8,
61 code: u8,
62 data: &'a [u8],
63 },
64}
65
66#[derive(Debug, Clone)]
68pub struct IcmpPacket<'a> {
69 buf: &'a [u8],
70}
71
72impl<'a> IcmpPacket<'a> {
73 pub fn new(buf: &'a [u8]) -> Option<Self> {
74 if buf.len() < ICMP_HEADER_LEN {
75 return None;
76 }
77 Some(Self { buf })
78 }
79
80 #[inline]
81 pub fn type_(&self) -> u8 {
82 self.buf[0]
83 }
84
85 #[inline]
86 pub fn code(&self) -> u8 {
87 self.buf[1]
88 }
89
90 #[inline]
91 pub fn checksum(&self) -> u16 {
92 read_u16be(&self.buf[2..4])
93 }
94
95 #[inline]
97 pub fn rest_of_header(&self) -> u32 {
98 read_u32be(&self.buf[4..8])
99 }
100
101 #[inline]
103 pub fn payload(&self) -> &'a [u8] {
104 &self.buf[ICMP_HEADER_LEN..]
105 }
106
107 pub fn verify_checksum(&self) -> bool {
109 internet_checksum(self.buf) == 0
110 }
111
112 pub fn message(&self) -> IcmpMessage<'a> {
114 let payload = self.payload();
115 let roh = self.rest_of_header();
116 match self.type_() {
117 ICMP_TYPE_ECHO_REPLY => IcmpMessage::EchoReply {
118 identifier: (roh >> 16) as u16,
119 sequence_number: roh as u16,
120 data: payload,
121 },
122 ICMP_TYPE_ECHO_REQUEST => IcmpMessage::EchoRequest {
123 identifier: (roh >> 16) as u16,
124 sequence_number: roh as u16,
125 data: payload,
126 },
127 ICMP_TYPE_DEST_UNREACH => IcmpMessage::DestUnreachable {
128 code: self.code(),
129 data: payload,
130 },
131 ICMP_TYPE_TIME_EXCEEDED => IcmpMessage::TimeExceeded {
132 code: self.code(),
133 data: payload,
134 },
135 _ => IcmpMessage::Unknown {
136 type_: self.type_(),
137 code: self.code(),
138 data: payload,
139 },
140 }
141 }
142}
143
144pub struct IcmpPacketBuilder {
149 buf: Vec<u8>,
150 payload: Option<Vec<u8>>,
151}
152
153impl Default for IcmpPacketBuilder {
154 fn default() -> Self {
155 Self::new(ICMP_TYPE_ECHO_REQUEST, 0)
156 }
157}
158
159impl IcmpPacketBuilder {
160 pub fn new(type_: u8, code: u8) -> Self {
161 let mut buf = vec![0u8; ICMP_HEADER_LEN];
162 buf[0] = type_;
163 buf[1] = code;
164 Self { buf, payload: None }
165 }
166
167 pub fn echo_request() -> Self {
169 Self::new(ICMP_TYPE_ECHO_REQUEST, 0)
170 }
171
172 pub fn echo_reply() -> Self {
174 Self::new(ICMP_TYPE_ECHO_REPLY, 0)
175 }
176
177 pub fn code(mut self, code: u8) -> Self {
178 self.buf[1] = code;
179 self
180 }
181
182 pub fn identifier(mut self, id: u16) -> Self {
183 write_u16be(&mut self.buf[4..6], id);
184 self
185 }
186
187 pub fn sequence_number(mut self, seq: u16) -> Self {
188 write_u16be(&mut self.buf[6..8], seq);
189 self
190 }
191
192 pub fn payload(mut self, data: &[u8]) -> Self {
193 self.payload = Some(data.to_vec());
194 self
195 }
196
197 pub fn build(mut self) -> Vec<u8> {
198 self.buf[2] = 0;
200 self.buf[3] = 0;
201
202 let mut packet = self.buf;
203 if let Some(ref p) = self.payload {
204 packet.extend_from_slice(p);
205 }
206 let csum = internet_checksum(&packet);
207 write_u16be(&mut packet[2..4], csum);
208 packet
209 }
210}
211
212#[derive(Debug, Clone)]
218pub enum Icmpv6Message<'a> {
219 EchoRequest {
220 identifier: u16,
221 sequence_number: u16,
222 data: &'a [u8],
223 },
224 EchoReply {
225 identifier: u16,
226 sequence_number: u16,
227 data: &'a [u8],
228 },
229 NeighborSolicitation {
230 target_address: Ipv6Addr,
231 options: &'a [u8],
232 },
233 NeighborAdvertisement {
234 flags: u8,
235 target_address: Ipv6Addr,
236 options: &'a [u8],
237 },
238 RouterSolicitation {
239 options: &'a [u8],
240 },
241 RouterAdvertisement {
242 hop_limit: u8,
243 flags: u8,
244 router_lifetime: u16,
245 reachable_time: u32,
246 retrans_timer: u32,
247 options: &'a [u8],
248 },
249 Unknown {
250 type_: u8,
251 code: u8,
252 data: &'a [u8],
253 },
254}
255
256#[derive(Debug, Clone)]
258pub struct Icmpv6Packet<'a> {
259 buf: &'a [u8],
260}
261
262impl<'a> Icmpv6Packet<'a> {
263 pub fn new(buf: &'a [u8]) -> Option<Self> {
264 if buf.len() < ICMPV6_HEADER_LEN {
265 return None;
266 }
267 Some(Self { buf })
268 }
269
270 #[inline]
271 pub fn type_(&self) -> u8 {
272 self.buf[0]
273 }
274
275 #[inline]
276 pub fn code(&self) -> u8 {
277 self.buf[1]
278 }
279
280 #[inline]
281 pub fn checksum(&self) -> u16 {
282 read_u16be(&self.buf[2..4])
283 }
284
285 #[inline]
287 pub fn body(&self) -> &'a [u8] {
288 &self.buf[ICMPV6_HEADER_LEN..]
289 }
290
291 pub fn verify_checksum(&self, src: Ipv6Addr, dst: Ipv6Addr) -> bool {
293 let csum = pseudo_header_checksum(
294 &src.octets(),
295 &dst.octets(),
296 u8::from(IpProtocol::Icmpv6),
297 self.buf,
298 );
299 csum == 0
300 }
301
302 pub fn message(&self) -> Icmpv6Message<'a> {
304 let buf = self.buf;
305 match self.type_() {
306 ICMPV6_TYPE_ECHO_REQUEST => {
307 let id = if buf.len() >= 6 { read_u16be(&buf[4..6]) } else { 0 };
308 let seq = if buf.len() >= 8 { read_u16be(&buf[6..8]) } else { 0 };
309 let data = if buf.len() > 8 { &buf[8..] } else { &[] };
310 Icmpv6Message::EchoRequest { identifier: id, sequence_number: seq, data }
311 }
312 ICMPV6_TYPE_ECHO_REPLY => {
313 let id = if buf.len() >= 6 { read_u16be(&buf[4..6]) } else { 0 };
314 let seq = if buf.len() >= 8 { read_u16be(&buf[6..8]) } else { 0 };
315 let data = if buf.len() > 8 { &buf[8..] } else { &[] };
316 Icmpv6Message::EchoReply { identifier: id, sequence_number: seq, data }
317 }
318 ICMPV6_TYPE_NEIGHBOR_SOLICIT => {
319 let target = if buf.len() >= 24 {
320 let b: [u8; 16] = buf[8..24].try_into().unwrap();
321 Ipv6Addr::from(b)
322 } else {
323 Ipv6Addr::UNSPECIFIED
324 };
325 let options = if buf.len() > 24 { &buf[24..] } else { &[] };
326 Icmpv6Message::NeighborSolicitation { target_address: target, options }
327 }
328 ICMPV6_TYPE_NEIGHBOR_ADVERT => {
329 let flags = if buf.len() >= 8 { buf[4] } else { 0 };
330 let target = if buf.len() >= 24 {
331 let b: [u8; 16] = buf[8..24].try_into().unwrap();
332 Ipv6Addr::from(b)
333 } else {
334 Ipv6Addr::UNSPECIFIED
335 };
336 let options = if buf.len() > 24 { &buf[24..] } else { &[] };
337 Icmpv6Message::NeighborAdvertisement { flags, target_address: target, options }
338 }
339 ICMPV6_TYPE_ROUTER_SOLICIT => {
340 let options = if buf.len() > 8 { &buf[8..] } else { &[] };
341 Icmpv6Message::RouterSolicitation { options }
342 }
343 ICMPV6_TYPE_ROUTER_ADVERT => {
344 let hop_limit = if buf.len() >= 8 { buf[4] } else { 0 };
345 let flags = if buf.len() >= 8 { buf[5] } else { 0 };
346 let router_lifetime = if buf.len() >= 8 { read_u16be(&buf[6..8]) } else { 0 };
347 let reachable_time = if buf.len() >= 12 { read_u32be(&buf[8..12]) } else { 0 };
348 let retrans_timer = if buf.len() >= 16 { read_u32be(&buf[12..16]) } else { 0 };
349 let options = if buf.len() > 16 { &buf[16..] } else { &[] };
350 Icmpv6Message::RouterAdvertisement {
351 hop_limit, flags, router_lifetime, reachable_time, retrans_timer, options,
352 }
353 }
354 _ => Icmpv6Message::Unknown {
355 type_: self.type_(),
356 code: self.code(),
357 data: if buf.len() > 8 { &buf[8..] } else { &[] },
358 },
359 }
360 }
361}
362
363pub struct Icmpv6PacketBuilder {
368 buf: Vec<u8>,
369 body: Vec<u8>,
370}
371
372impl Default for Icmpv6PacketBuilder {
373 fn default() -> Self {
374 Self::new(ICMPV6_TYPE_ECHO_REQUEST, 0)
375 }
376}
377
378impl Icmpv6PacketBuilder {
379 pub fn new(type_: u8, code: u8) -> Self {
380 let mut buf = vec![0u8; ICMPV6_HEADER_LEN];
381 buf[0] = type_;
382 buf[1] = code;
383 Self { buf, body: Vec::new() }
384 }
385
386 pub fn echo_request() -> Self {
387 Self::new(ICMPV6_TYPE_ECHO_REQUEST, 0)
388 }
389
390 pub fn echo_reply() -> Self {
391 Self::new(ICMPV6_TYPE_ECHO_REPLY, 0)
392 }
393
394 pub fn code(mut self, code: u8) -> Self {
395 self.buf[1] = code;
396 self
397 }
398
399 pub fn identifier(mut self, id: u16) -> Self {
401 write_u16be(&mut self.buf[4..6], id);
402 self
403 }
404
405 pub fn sequence_number(mut self, seq: u16) -> Self {
407 write_u16be(&mut self.buf[6..8], seq);
408 self
409 }
410
411 pub fn body(mut self, data: &[u8]) -> Self {
413 self.body = data.to_vec();
414 self
415 }
416
417 pub fn build(mut self, src: Ipv6Addr, dst: Ipv6Addr) -> Vec<u8> {
419 self.buf[2] = 0;
420 self.buf[3] = 0;
421
422 let mut packet = self.buf;
423 packet.extend_from_slice(&self.body);
424 let csum = pseudo_header_checksum(
425 &src.octets(),
426 &dst.octets(),
427 u8::from(IpProtocol::Icmpv6),
428 &packet,
429 );
430 write_u16be(&mut packet[2..4], csum);
431 packet
432 }
433}
434
435#[cfg(test)]
440mod tests {
441 use super::*;
442 use core::net::Ipv6Addr;
443
444 #[test]
447 fn parse_icmp_echo_request() {
448 let data: &[u8] = &[
450 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x41, 0x42, 0x43, 0x44, ];
454 let pkt = IcmpPacket::new(data).unwrap();
455 assert_eq!(pkt.type_(), 8);
456 assert_eq!(pkt.code(), 0);
457 assert_eq!(pkt.payload(), &[0x41, 0x42, 0x43, 0x44]);
458
459 match pkt.message() {
460 IcmpMessage::EchoRequest { identifier, sequence_number, data } => {
461 assert_eq!(identifier, 1);
462 assert_eq!(sequence_number, 2);
463 assert_eq!(data, &[0x41, 0x42, 0x43, 0x44]);
464 }
465 _ => panic!("expected EchoRequest"),
466 }
467 }
468
469 #[test]
470 fn parse_icmp_echo_reply() {
471 let data: &[u8] = &[
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, ];
475 let pkt = IcmpPacket::new(data).unwrap();
476 match pkt.message() {
477 IcmpMessage::EchoReply { identifier, sequence_number, .. } => {
478 assert_eq!(identifier, 3);
479 assert_eq!(sequence_number, 4);
480 }
481 _ => panic!("expected EchoReply"),
482 }
483 }
484
485 #[test]
486 fn parse_icmp_dest_unreachable() {
487 let data: &[u8] = &[
488 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, ];
492 let pkt = IcmpPacket::new(data).unwrap();
493 match pkt.message() {
494 IcmpMessage::DestUnreachable { code, data: d } => {
495 assert_eq!(code, 1);
496 assert_eq!(d, &[0x11, 0x22]);
497 }
498 _ => panic!("expected DestUnreachable"),
499 }
500 }
501
502 #[test]
503 fn parse_icmp_time_exceeded() {
504 let data: &[u8] = &[
505 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 ];
508 let pkt = IcmpPacket::new(data).unwrap();
509 match pkt.message() {
510 IcmpMessage::TimeExceeded { code, .. } => {
511 assert_eq!(code, 0);
512 }
513 _ => panic!("expected TimeExceeded"),
514 }
515 }
516
517 #[test]
518 fn parse_icmp_too_short() {
519 assert!(IcmpPacket::new(&[]).is_none());
520 assert!(IcmpPacket::new(&[0u8; 7]).is_none());
521 assert!(IcmpPacket::new(&[0u8; 8]).is_some());
522 }
523
524 #[test]
525 fn icmp_echo_build_and_verify_checksum() {
526 let pkt_bytes = IcmpPacketBuilder::echo_request()
527 .identifier(0x1234)
528 .sequence_number(1)
529 .payload(&[0xAA, 0xBB, 0xCC, 0xDD])
530 .build();
531
532 let pkt = IcmpPacket::new(&pkt_bytes).unwrap();
533 assert!(pkt.verify_checksum());
534 match pkt.message() {
535 IcmpMessage::EchoRequest { identifier, sequence_number, .. } => {
536 assert_eq!(identifier, 0x1234);
537 assert_eq!(sequence_number, 1);
538 }
539 _ => panic!("expected EchoRequest"),
540 }
541 }
542
543 #[test]
546 fn parse_icmpv6_echo_request() {
547 let data: &[u8] = &[
548 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0xDE, 0xAD, ];
552 let pkt = Icmpv6Packet::new(data).unwrap();
553 assert_eq!(pkt.type_(), 128);
554 match pkt.message() {
555 Icmpv6Message::EchoRequest { identifier, sequence_number, data } => {
556 assert_eq!(identifier, 5);
557 assert_eq!(sequence_number, 6);
558 assert_eq!(data, &[0xDE, 0xAD]);
559 }
560 _ => panic!("expected EchoRequest"),
561 }
562 }
563
564 #[test]
565 fn parse_icmpv6_echo_reply() {
566 let data: &[u8] = &[
567 0x81, 0x00, 0x00, 0x00,
568 0x00, 0x07, 0x00, 0x08,
569 ];
570 let pkt = Icmpv6Packet::new(data).unwrap();
571 match pkt.message() {
572 Icmpv6Message::EchoReply { identifier, sequence_number, .. } => {
573 assert_eq!(identifier, 7);
574 assert_eq!(sequence_number, 8);
575 }
576 _ => panic!("expected EchoReply"),
577 }
578 }
579
580 #[test]
581 fn parse_icmpv6_neighbor_solicitation() {
582 let target = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
583 let mut data = vec![0x87u8, 0x00, 0x00, 0x00]; data.extend_from_slice(&[0u8; 4]); data.extend_from_slice(&target.octets()); data.extend_from_slice(&[0x01, 0x01, 0x02, 0x03]); let pkt = Icmpv6Packet::new(&data).unwrap();
589 match pkt.message() {
590 Icmpv6Message::NeighborSolicitation { target_address, options } => {
591 assert_eq!(target_address, target);
592 assert_eq!(options.len(), 4);
593 }
594 _ => panic!("expected NeighborSolicitation"),
595 }
596 }
597
598 #[test]
599 fn parse_icmpv6_neighbor_advertisement() {
600 let target = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
601 let mut data = vec![0x88u8, 0x00, 0x00, 0x00]; data.push(0x60); data.extend_from_slice(&[0u8; 3]); data.extend_from_slice(&target.octets()); let pkt = Icmpv6Packet::new(&data).unwrap();
607 match pkt.message() {
608 Icmpv6Message::NeighborAdvertisement { flags, target_address, .. } => {
609 assert_eq!(flags, 0x60);
610 assert_eq!(target_address, target);
611 }
612 _ => panic!("expected NeighborAdvertisement"),
613 }
614 }
615
616 #[test]
617 fn parse_icmpv6_router_solicitation() {
618 let data: &[u8] = &[
619 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, ];
623 let pkt = Icmpv6Packet::new(data).unwrap();
624 match pkt.message() {
625 Icmpv6Message::RouterSolicitation { options } => {
626 assert_eq!(options.len(), 2);
627 }
628 _ => panic!("expected RouterSolicitation"),
629 }
630 }
631
632 #[test]
633 fn parse_icmpv6_router_advertisement() {
634 let mut data = vec![0x86u8, 0x00, 0x00, 0x00]; data.push(64); data.push(0x08); data.extend_from_slice(&0x0E10u16.to_be_bytes()); data.extend_from_slice(&0x0000EA60u32.to_be_bytes()); data.extend_from_slice(&0x00002710u32.to_be_bytes()); data.extend_from_slice(&[0x01, 0x01]); let pkt = Icmpv6Packet::new(&data).unwrap();
643 match pkt.message() {
644 Icmpv6Message::RouterAdvertisement {
645 hop_limit, flags, router_lifetime,
646 reachable_time, retrans_timer, options,
647 } => {
648 assert_eq!(hop_limit, 64);
649 assert_eq!(flags, 0x08);
650 assert_eq!(router_lifetime, 3600);
651 assert_eq!(reachable_time, 60000);
652 assert_eq!(retrans_timer, 10000);
653 assert_eq!(options.len(), 2);
654 }
655 _ => panic!("expected RouterAdvertisement"),
656 }
657 }
658
659 #[test]
660 fn parse_icmpv6_too_short() {
661 assert!(Icmpv6Packet::new(&[]).is_none());
662 assert!(Icmpv6Packet::new(&[0u8; 7]).is_none());
663 assert!(Icmpv6Packet::new(&[0u8; 8]).is_some());
664 }
665
666 #[test]
667 fn icmpv6_echo_build_and_verify_checksum() {
668 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
669 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
670 let pkt_bytes = Icmpv6PacketBuilder::echo_request()
671 .identifier(0x42)
672 .sequence_number(1)
673 .build(src, dst);
674
675 let pkt = Icmpv6Packet::new(&pkt_bytes).unwrap();
676 assert!(pkt.verify_checksum(src, dst));
677 match pkt.message() {
678 Icmpv6Message::EchoRequest { identifier, sequence_number, .. } => {
679 assert_eq!(identifier, 0x42);
680 assert_eq!(sequence_number, 1);
681 }
682 _ => panic!("expected EchoRequest"),
683 }
684 }
685}