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