1pub mod builder;
8
9pub use builder::Icmpv6Builder;
10
11use crate::layer::field::{FieldError, FieldValue};
12use crate::layer::{Layer, LayerIndex, LayerKind};
13use std::net::Ipv6Addr;
14
15pub const ICMPV6_MIN_HEADER_LEN: usize = 8;
17
18pub mod offsets {
20 pub const TYPE: usize = 0;
22 pub const CODE: usize = 1;
24 pub const CHECKSUM: usize = 2;
26 pub const ID: usize = 4;
29 pub const SEQ: usize = 6;
30 pub const TARGET_ADDR: usize = 8;
32 pub const MTU: usize = 4;
34}
35
36pub mod types {
38 pub const DEST_UNREACH: u8 = 1;
40 pub const PKT_TOO_BIG: u8 = 2;
42 pub const TIME_EXCEEDED: u8 = 3;
44 pub const PARAM_PROBLEM: u8 = 4;
46 pub const ECHO_REQUEST: u8 = 128;
48 pub const ECHO_REPLY: u8 = 129;
50 pub const ROUTER_SOLICIT: u8 = 133;
52 pub const ROUTER_ADVERT: u8 = 134;
54 pub const NEIGHBOR_SOLICIT: u8 = 135;
56 pub const NEIGHBOR_ADVERT: u8 = 136;
58 pub const REDIRECT: u8 = 137;
60
61 #[must_use]
63 pub fn name(t: u8) -> &'static str {
64 match t {
65 DEST_UNREACH => "dest-unreach",
66 PKT_TOO_BIG => "pkt-too-big",
67 TIME_EXCEEDED => "time-exceeded",
68 PARAM_PROBLEM => "param-problem",
69 ECHO_REQUEST => "echo-request",
70 ECHO_REPLY => "echo-reply",
71 ROUTER_SOLICIT => "router-solicit",
72 ROUTER_ADVERT => "router-advert",
73 NEIGHBOR_SOLICIT => "neighbor-solicit",
74 NEIGHBOR_ADVERT => "neighbor-advert",
75 REDIRECT => "redirect",
76 _ => "unknown",
77 }
78 }
79}
80
81#[must_use]
90pub fn icmpv6_checksum(src: Ipv6Addr, dst: Ipv6Addr, icmpv6_data: &[u8]) -> u16 {
91 let mut sum: u32 = 0;
92
93 let src_bytes = src.octets();
95 let dst_bytes = dst.octets();
96
97 for chunk in src_bytes.chunks(2) {
99 sum += u32::from(u16::from_be_bytes([chunk[0], chunk[1]]));
100 }
101
102 for chunk in dst_bytes.chunks(2) {
104 sum += u32::from(u16::from_be_bytes([chunk[0], chunk[1]]));
105 }
106
107 let upper_len = icmpv6_data.len() as u32;
109 sum += upper_len >> 16;
110 sum += upper_len & 0xFFFF;
111
112 sum += 0x003Au32; let mut chunks = icmpv6_data.chunks_exact(2);
119 for chunk in &mut chunks {
120 sum += u32::from(u16::from_be_bytes([chunk[0], chunk[1]]));
121 }
122 if let Some(&last) = chunks.remainder().first() {
124 sum += u32::from(last) << 8;
125 }
126
127 while sum >> 16 != 0 {
129 sum = (sum & 0xFFFF) + (sum >> 16);
130 }
131
132 !(sum as u16)
133}
134
135#[must_use]
140pub fn verify_icmpv6_checksum(src: Ipv6Addr, dst: Ipv6Addr, icmpv6_data: &[u8]) -> bool {
141 let result = icmpv6_checksum(src, dst, icmpv6_data);
142 result == 0 || result == 0xFFFF
143}
144
145#[derive(Debug, Clone)]
150pub struct Icmpv6Layer {
151 pub index: LayerIndex,
152}
153
154impl Icmpv6Layer {
155 #[must_use]
157 pub fn new(index: LayerIndex) -> Self {
158 Self { index }
159 }
160
161 #[must_use]
163 pub const fn at_start() -> Self {
164 Self {
165 index: LayerIndex::new(LayerKind::Icmpv6, 0, ICMPV6_MIN_HEADER_LEN),
166 }
167 }
168
169 pub fn icmpv6_type(&self, buf: &[u8]) -> Result<u8, FieldError> {
173 let slice = self.index.slice(buf);
174 if slice.is_empty() {
175 return Err(FieldError::BufferTooShort {
176 offset: self.index.start + offsets::TYPE,
177 need: 1,
178 have: 0,
179 });
180 }
181 Ok(slice[offsets::TYPE])
182 }
183
184 pub fn code(&self, buf: &[u8]) -> Result<u8, FieldError> {
186 let slice = self.index.slice(buf);
187 if slice.len() < offsets::CODE + 1 {
188 return Err(FieldError::BufferTooShort {
189 offset: self.index.start + offsets::CODE,
190 need: 1,
191 have: slice.len().saturating_sub(offsets::CODE),
192 });
193 }
194 Ok(slice[offsets::CODE])
195 }
196
197 pub fn checksum(&self, buf: &[u8]) -> Result<u16, FieldError> {
199 let slice = self.index.slice(buf);
200 if slice.len() < offsets::CHECKSUM + 2 {
201 return Err(FieldError::BufferTooShort {
202 offset: self.index.start + offsets::CHECKSUM,
203 need: 2,
204 have: slice.len().saturating_sub(offsets::CHECKSUM),
205 });
206 }
207 Ok(u16::from_be_bytes([
208 slice[offsets::CHECKSUM],
209 slice[offsets::CHECKSUM + 1],
210 ]))
211 }
212
213 pub fn id(&self, buf: &[u8]) -> Result<Option<u16>, FieldError> {
217 let icmpv6_type = self.icmpv6_type(buf)?;
218 if !matches!(icmpv6_type, types::ECHO_REQUEST | types::ECHO_REPLY) {
219 return Ok(None);
220 }
221 let slice = self.index.slice(buf);
222 if slice.len() < offsets::ID + 2 {
223 return Err(FieldError::BufferTooShort {
224 offset: self.index.start + offsets::ID,
225 need: 2,
226 have: slice.len().saturating_sub(offsets::ID),
227 });
228 }
229 Ok(Some(u16::from_be_bytes([
230 slice[offsets::ID],
231 slice[offsets::ID + 1],
232 ])))
233 }
234
235 pub fn seq(&self, buf: &[u8]) -> Result<Option<u16>, FieldError> {
239 let icmpv6_type = self.icmpv6_type(buf)?;
240 if !matches!(icmpv6_type, types::ECHO_REQUEST | types::ECHO_REPLY) {
241 return Ok(None);
242 }
243 let slice = self.index.slice(buf);
244 if slice.len() < offsets::SEQ + 2 {
245 return Err(FieldError::BufferTooShort {
246 offset: self.index.start + offsets::SEQ,
247 need: 2,
248 have: slice.len().saturating_sub(offsets::SEQ),
249 });
250 }
251 Ok(Some(u16::from_be_bytes([
252 slice[offsets::SEQ],
253 slice[offsets::SEQ + 1],
254 ])))
255 }
256
257 pub fn target_addr(&self, buf: &[u8]) -> Result<Option<Ipv6Addr>, FieldError> {
261 let icmpv6_type = self.icmpv6_type(buf)?;
262 if !matches!(
263 icmpv6_type,
264 types::NEIGHBOR_SOLICIT | types::NEIGHBOR_ADVERT | types::REDIRECT
265 ) {
266 return Ok(None);
267 }
268 let start = self.index.start + offsets::TARGET_ADDR;
269 if buf.len() < start + 16 {
270 return Err(FieldError::BufferTooShort {
271 offset: start,
272 need: 16,
273 have: buf.len().saturating_sub(start),
274 });
275 }
276 let mut addr_bytes = [0u8; 16];
277 addr_bytes.copy_from_slice(&buf[start..start + 16]);
278 Ok(Some(Ipv6Addr::from(addr_bytes)))
279 }
280
281 pub fn mtu(&self, buf: &[u8]) -> Result<Option<u32>, FieldError> {
285 let icmpv6_type = self.icmpv6_type(buf)?;
286 if icmpv6_type != types::PKT_TOO_BIG {
287 return Ok(None);
288 }
289 let slice = self.index.slice(buf);
290 if slice.len() < offsets::MTU + 4 {
291 return Err(FieldError::BufferTooShort {
292 offset: self.index.start + offsets::MTU,
293 need: 4,
294 have: slice.len().saturating_sub(offsets::MTU),
295 });
296 }
297 Ok(Some(u32::from_be_bytes([
298 slice[offsets::MTU],
299 slice[offsets::MTU + 1],
300 slice[offsets::MTU + 2],
301 slice[offsets::MTU + 3],
302 ])))
303 }
304
305 pub fn set_type(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
309 let offset = self.index.start + offsets::TYPE;
310 if buf.len() <= offset {
311 return Err(FieldError::BufferTooShort {
312 offset,
313 need: 1,
314 have: buf.len().saturating_sub(offset),
315 });
316 }
317 buf[offset] = value;
318 Ok(())
319 }
320
321 pub fn set_code(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
323 let offset = self.index.start + offsets::CODE;
324 if buf.len() <= offset {
325 return Err(FieldError::BufferTooShort {
326 offset,
327 need: 1,
328 have: buf.len().saturating_sub(offset),
329 });
330 }
331 buf[offset] = value;
332 Ok(())
333 }
334
335 pub fn set_checksum(&self, buf: &mut [u8], value: u16) -> Result<(), FieldError> {
337 let offset = self.index.start + offsets::CHECKSUM;
338 if buf.len() < offset + 2 {
339 return Err(FieldError::BufferTooShort {
340 offset,
341 need: 2,
342 have: buf.len().saturating_sub(offset),
343 });
344 }
345 buf[offset..offset + 2].copy_from_slice(&value.to_be_bytes());
346 Ok(())
347 }
348
349 pub fn set_id(&self, buf: &mut [u8], value: u16) -> Result<(), FieldError> {
351 let offset = self.index.start + offsets::ID;
352 if buf.len() < offset + 2 {
353 return Err(FieldError::BufferTooShort {
354 offset,
355 need: 2,
356 have: buf.len().saturating_sub(offset),
357 });
358 }
359 buf[offset..offset + 2].copy_from_slice(&value.to_be_bytes());
360 Ok(())
361 }
362
363 pub fn set_seq(&self, buf: &mut [u8], value: u16) -> Result<(), FieldError> {
365 let offset = self.index.start + offsets::SEQ;
366 if buf.len() < offset + 2 {
367 return Err(FieldError::BufferTooShort {
368 offset,
369 need: 2,
370 have: buf.len().saturating_sub(offset),
371 });
372 }
373 buf[offset..offset + 2].copy_from_slice(&value.to_be_bytes());
374 Ok(())
375 }
376
377 pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
381 match name {
382 "type" => Some(self.icmpv6_type(buf).map(FieldValue::U8)),
383 "code" => Some(self.code(buf).map(FieldValue::U8)),
384 "chksum" => Some(self.checksum(buf).map(FieldValue::U16)),
385 "id" => Some(
386 self.id(buf)
387 .and_then(|opt| {
388 opt.ok_or(FieldError::InvalidValue(
389 "id field not available for this ICMPv6 type".into(),
390 ))
391 })
392 .map(FieldValue::U16),
393 ),
394 "seq" => Some(
395 self.seq(buf)
396 .and_then(|opt| {
397 opt.ok_or(FieldError::InvalidValue(
398 "seq field not available for this ICMPv6 type".into(),
399 ))
400 })
401 .map(FieldValue::U16),
402 ),
403 _ => None,
404 }
405 }
406
407 pub fn set_field(
409 &self,
410 buf: &mut [u8],
411 name: &str,
412 value: FieldValue,
413 ) -> Option<Result<(), FieldError>> {
414 match (name, value) {
415 ("type", FieldValue::U8(v)) => Some(self.set_type(buf, v)),
416 ("code", FieldValue::U8(v)) => Some(self.set_code(buf, v)),
417 ("chksum", FieldValue::U16(v)) => Some(self.set_checksum(buf, v)),
418 ("id", FieldValue::U16(v)) => Some(self.set_id(buf, v)),
419 ("seq", FieldValue::U16(v)) => Some(self.set_seq(buf, v)),
420 (name, value) => Some(Err(FieldError::InvalidValue(format!(
421 "unknown field '{name}' or type mismatch for {value:?}"
422 )))),
423 }
424 }
425
426 #[must_use]
428 pub fn field_names() -> &'static [&'static str] {
429 &["type", "code", "chksum", "id", "seq"]
430 }
431
432 #[must_use]
436 pub fn summary(&self, buf: &[u8]) -> String {
437 if let (Ok(icmpv6_type), Ok(code)) = (self.icmpv6_type(buf), self.code(buf)) {
438 let type_name = types::name(icmpv6_type);
439
440 let details = match icmpv6_type {
441 types::ECHO_REQUEST | types::ECHO_REPLY => {
442 let id_str = self
443 .id(buf)
444 .ok()
445 .flatten()
446 .map(|id| format!(" id={id:#06x}"))
447 .unwrap_or_default();
448 let seq_str = self
449 .seq(buf)
450 .ok()
451 .flatten()
452 .map(|seq| format!(" seq={seq}"))
453 .unwrap_or_default();
454 format!("{id_str}{seq_str}")
455 },
456 types::PKT_TOO_BIG => self
457 .mtu(buf)
458 .ok()
459 .flatten()
460 .map(|mtu| format!(" mtu={mtu}"))
461 .unwrap_or_default(),
462 types::NEIGHBOR_SOLICIT | types::NEIGHBOR_ADVERT => self
463 .target_addr(buf)
464 .ok()
465 .flatten()
466 .map(|addr| format!(" target={addr}"))
467 .unwrap_or_default(),
468 _ => String::new(),
469 };
470
471 if code == 0 {
472 format!("ICMPv6 {type_name}{details}")
473 } else {
474 format!("ICMPv6 {type_name} code={code}{details}")
475 }
476 } else {
477 "ICMPv6".to_string()
478 }
479 }
480
481 #[must_use]
487 pub fn header_len(&self, _buf: &[u8]) -> usize {
488 ICMPV6_MIN_HEADER_LEN
489 }
490
491 #[must_use]
493 pub fn hashret(&self, buf: &[u8]) -> Vec<u8> {
494 let icmpv6_type = self.icmpv6_type(buf).unwrap_or(0);
495 match icmpv6_type {
496 types::ECHO_REQUEST | types::ECHO_REPLY => {
497 let id = self.id(buf).ok().flatten().unwrap_or(0);
499 let seq = self.seq(buf).ok().flatten().unwrap_or(0);
500 vec![
501 (id >> 8) as u8,
502 (id & 0xFF) as u8,
503 (seq >> 8) as u8,
504 (seq & 0xFF) as u8,
505 ]
506 },
507 _ => vec![],
508 }
509 }
510
511 #[must_use]
513 pub fn answers(&self, buf: &[u8], other: &Icmpv6Layer, other_buf: &[u8]) -> bool {
514 let self_type = self.icmpv6_type(buf).unwrap_or(0);
515 let other_type = other.icmpv6_type(other_buf).unwrap_or(0);
516
517 match (self_type, other_type) {
518 (types::ECHO_REPLY, types::ECHO_REQUEST) => {
519 let self_id = self.id(buf).ok().flatten();
521 let other_id = other.id(other_buf).ok().flatten();
522 let self_seq = self.seq(buf).ok().flatten();
523 let other_seq = other.seq(other_buf).ok().flatten();
524 self_id == other_id && self_seq == other_seq
525 },
526 _ => false,
527 }
528 }
529}
530
531impl Layer for Icmpv6Layer {
532 fn kind(&self) -> LayerKind {
533 LayerKind::Icmpv6
534 }
535
536 fn summary(&self, data: &[u8]) -> String {
537 self.summary(data)
538 }
539
540 fn header_len(&self, data: &[u8]) -> usize {
541 self.header_len(data)
542 }
543
544 fn hashret(&self, data: &[u8]) -> Vec<u8> {
545 self.hashret(data)
546 }
547
548 fn answers(&self, data: &[u8], other: &Self, other_data: &[u8]) -> bool {
549 self.answers(data, other, other_data)
550 }
551
552 fn field_names(&self) -> &'static [&'static str] {
553 Self::field_names()
554 }
555}
556
557#[cfg(test)]
558mod tests {
559 use super::*;
560
561 fn make_echo_request(id: u16, seq: u16) -> Vec<u8> {
562 let mut buf = vec![0u8; 8];
563 buf[0] = types::ECHO_REQUEST;
564 buf[1] = 0; buf[2] = 0; buf[3] = 0;
567 buf[4] = (id >> 8) as u8;
568 buf[5] = (id & 0xFF) as u8;
569 buf[6] = (seq >> 8) as u8;
570 buf[7] = (seq & 0xFF) as u8;
571 buf
572 }
573
574 fn make_echo_reply(id: u16, seq: u16) -> Vec<u8> {
575 let mut buf = make_echo_request(id, seq);
576 buf[0] = types::ECHO_REPLY;
577 buf
578 }
579
580 fn make_ns(target: Ipv6Addr) -> Vec<u8> {
581 let mut buf = vec![0u8; 24]; buf[0] = types::NEIGHBOR_SOLICIT;
583 buf[1] = 0;
584 buf[8..24].copy_from_slice(&target.octets());
586 buf
587 }
588
589 #[test]
590 fn test_icmpv6_type_echo_request() {
591 let buf = make_echo_request(0x1234, 5);
592 let layer = Icmpv6Layer::at_start();
593 assert_eq!(layer.icmpv6_type(&buf).unwrap(), types::ECHO_REQUEST);
594 }
595
596 #[test]
597 fn test_icmpv6_code() {
598 let buf = make_echo_request(1, 1);
599 let layer = Icmpv6Layer::at_start();
600 assert_eq!(layer.code(&buf).unwrap(), 0);
601 }
602
603 #[test]
604 fn test_icmpv6_id_seq() {
605 let buf = make_echo_request(0x5678, 42);
606 let layer = Icmpv6Layer::at_start();
607 assert_eq!(layer.id(&buf).unwrap(), Some(0x5678));
608 assert_eq!(layer.seq(&buf).unwrap(), Some(42));
609 }
610
611 #[test]
612 fn test_icmpv6_id_seq_not_available() {
613 let mut buf = vec![0u8; 8];
614 buf[0] = types::NEIGHBOR_SOLICIT;
615 let layer = Icmpv6Layer::at_start();
616 assert_eq!(layer.id(&buf).unwrap(), None);
618 assert_eq!(layer.seq(&buf).unwrap(), None);
619 }
620
621 #[test]
622 fn test_icmpv6_target_addr() {
623 let target = Ipv6Addr::new(0xfe80, 0, 0, 0, 1, 0, 0, 1);
624 let mut buf = make_ns(target);
625 let layer = Icmpv6Layer::new(LayerIndex::new(LayerKind::Icmpv6, 0, buf.len()));
627 let addr = layer.target_addr(&buf).unwrap();
628 assert_eq!(addr, Some(target));
629 }
630
631 #[test]
632 fn test_icmpv6_mtu() {
633 let mut buf = vec![0u8; 8];
634 buf[0] = types::PKT_TOO_BIG;
635 buf[4] = 0x00;
636 buf[5] = 0x00;
637 buf[6] = 0x05;
638 buf[7] = 0xDC; let layer = Icmpv6Layer::at_start();
640 assert_eq!(layer.mtu(&buf).unwrap(), Some(1500));
641 }
642
643 #[test]
644 fn test_icmpv6_mtu_not_available() {
645 let buf = make_echo_request(1, 1);
646 let layer = Icmpv6Layer::at_start();
647 assert_eq!(layer.mtu(&buf).unwrap(), None);
648 }
649
650 #[test]
651 fn test_icmpv6_summary_echo_request() {
652 let buf = make_echo_request(0x1234, 5);
653 let layer = Icmpv6Layer::at_start();
654 let s = layer.summary(&buf);
655 assert!(s.contains("echo-request"));
656 assert!(s.contains("0x1234"));
657 assert!(s.contains("5"));
658 }
659
660 #[test]
661 fn test_icmpv6_summary_ns() {
662 let target = Ipv6Addr::new(0xfe80, 0, 0, 0, 1, 0, 0, 1);
663 let buf = make_ns(target);
664 let layer = Icmpv6Layer::new(LayerIndex::new(LayerKind::Icmpv6, 0, buf.len()));
665 let s = layer.summary(&buf);
666 assert!(s.contains("neighbor-solicit"));
667 }
668
669 #[test]
670 fn test_icmpv6_answers_echo() {
671 let req_buf = make_echo_request(0xABCD, 10);
672 let rep_buf = make_echo_reply(0xABCD, 10);
673
674 let req_layer = Icmpv6Layer::at_start();
675 let rep_layer = Icmpv6Layer::at_start();
676
677 assert!(rep_layer.answers(&rep_buf, &req_layer, &req_buf));
678 }
679
680 #[test]
681 fn test_icmpv6_answers_echo_wrong_id() {
682 let req_buf = make_echo_request(0xABCD, 10);
683 let rep_buf = make_echo_reply(0x0001, 10);
684
685 let req_layer = Icmpv6Layer::at_start();
686 let rep_layer = Icmpv6Layer::at_start();
687
688 assert!(!rep_layer.answers(&rep_buf, &req_layer, &req_buf));
689 }
690
691 #[test]
692 fn test_icmpv6_checksum() {
693 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
694 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
695
696 let mut buf = make_echo_request(0x1234, 1);
698 let chksum = icmpv6_checksum(src, dst, &buf);
700 buf[2] = (chksum >> 8) as u8;
701 buf[3] = (chksum & 0xFF) as u8;
702
703 assert!(verify_icmpv6_checksum(src, dst, &buf));
705 }
706
707 #[test]
708 fn test_icmpv6_checksum_wrong() {
709 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
710 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
711 let buf = make_echo_request(0x1234, 1);
712 let _ = verify_icmpv6_checksum(src, dst, &buf);
715 }
716
717 #[test]
718 fn test_icmpv6_get_field() {
719 let buf = make_echo_request(0x5678, 3);
720 let layer = Icmpv6Layer::at_start();
721
722 if let Some(Ok(FieldValue::U8(t))) = layer.get_field(&buf, "type") {
723 assert_eq!(t, types::ECHO_REQUEST);
724 } else {
725 panic!("expected type field");
726 }
727
728 if let Some(Ok(FieldValue::U16(id))) = layer.get_field(&buf, "id") {
729 assert_eq!(id, 0x5678);
730 } else {
731 panic!("expected id field");
732 }
733 }
734
735 #[test]
736 fn test_icmpv6_set_field() {
737 let mut buf = make_echo_request(1, 1);
738 let layer = Icmpv6Layer::at_start();
739 layer
740 .set_field(&mut buf, "id", FieldValue::U16(0xDEAD))
741 .unwrap()
742 .unwrap();
743 assert_eq!(layer.id(&buf).unwrap(), Some(0xDEAD));
744 }
745
746 #[test]
747 fn test_icmpv6_header_len() {
748 let buf = make_echo_request(1, 1);
749 let layer = Icmpv6Layer::at_start();
750 assert_eq!(layer.header_len(&buf), ICMPV6_MIN_HEADER_LEN);
751 assert_eq!(ICMPV6_MIN_HEADER_LEN, 8);
752 }
753
754 #[test]
755 fn test_icmpv6_field_names() {
756 let names = Icmpv6Layer::field_names();
757 assert!(names.contains(&"type"));
758 assert!(names.contains(&"code"));
759 assert!(names.contains(&"chksum"));
760 assert!(names.contains(&"id"));
761 assert!(names.contains(&"seq"));
762 }
763}