1use std::net::Ipv6Addr;
4
5use compact_str::CompactString;
6use smallvec::SmallVec;
7
8use super::{FieldValue, ParseContext, ParseResult, Protocol};
9use crate::schema::{DataKind, FieldDescriptor};
10
11pub const IP_PROTO_ICMPV6: u8 = 58;
13
14pub mod icmpv6_type {
16 pub const DESTINATION_UNREACHABLE: u8 = 1;
18 pub const PACKET_TOO_BIG: u8 = 2;
19 pub const TIME_EXCEEDED: u8 = 3;
20 pub const PARAMETER_PROBLEM: u8 = 4;
21
22 pub const ECHO_REQUEST: u8 = 128;
24 pub const ECHO_REPLY: u8 = 129;
25
26 pub const MLD_QUERY: u8 = 130;
28 pub const MLDV1_REPORT: u8 = 131;
29 pub const MLDV1_DONE: u8 = 132;
30 pub const MLDV2_REPORT: u8 = 143;
31
32 pub const ROUTER_SOLICITATION: u8 = 133;
34 pub const ROUTER_ADVERTISEMENT: u8 = 134;
35 pub const NEIGHBOR_SOLICITATION: u8 = 135;
36 pub const NEIGHBOR_ADVERTISEMENT: u8 = 136;
37 pub const REDIRECT: u8 = 137;
38}
39
40pub mod ndp_option {
42 pub const SOURCE_LINK_LAYER_ADDR: u8 = 1;
43 pub const TARGET_LINK_LAYER_ADDR: u8 = 2;
44 pub const PREFIX_INFO: u8 = 3;
45 pub const MTU: u8 = 5;
46}
47
48#[derive(Debug, Clone, Copy)]
50pub struct Icmpv6Protocol;
51
52impl Protocol for Icmpv6Protocol {
53 fn name(&self) -> &'static str {
54 "icmpv6"
55 }
56
57 fn display_name(&self) -> &'static str {
58 "ICMPv6"
59 }
60
61 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
62 match context.hint("ip_protocol") {
64 Some(proto) if proto == IP_PROTO_ICMPV6 as u64 => {
65 match context.hint("ip_version") {
67 Some(6) => Some(100),
68 _ => None,
69 }
70 }
71 _ => None,
72 }
73 }
74
75 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
76 if data.len() < 4 {
78 return ParseResult::error(
79 format!("ICMPv6 header too short: {} bytes", data.len()),
80 data,
81 );
82 }
83
84 let mut fields = SmallVec::new();
85
86 let icmpv6_type = data[0];
87 let icmpv6_code = data[1];
88 let checksum = u16::from_be_bytes([data[2], data[3]]);
89
90 fields.push(("type", FieldValue::UInt8(icmpv6_type)));
91 fields.push(("code", FieldValue::UInt8(icmpv6_code)));
92 fields.push(("checksum", FieldValue::UInt16(checksum)));
93
94 let type_name = get_type_name(icmpv6_type);
96 fields.push(("type_name", FieldValue::Str(type_name)));
97
98 let consumed = match icmpv6_type {
100 icmpv6_type::ECHO_REQUEST | icmpv6_type::ECHO_REPLY => {
101 parse_echo(&data[4..], &mut fields)
102 }
103 icmpv6_type::PACKET_TOO_BIG => parse_packet_too_big(&data[4..], &mut fields),
104 icmpv6_type::PARAMETER_PROBLEM => parse_parameter_problem(&data[4..], &mut fields),
105 icmpv6_type::DESTINATION_UNREACHABLE | icmpv6_type::TIME_EXCEEDED => {
106 if data.len() >= 8 {
108 8
109 } else {
110 4
111 }
112 }
113 icmpv6_type::ROUTER_SOLICITATION => parse_router_solicitation(&data[4..], &mut fields),
115 icmpv6_type::ROUTER_ADVERTISEMENT => {
116 parse_router_advertisement(&data[4..], &mut fields)
117 }
118 icmpv6_type::NEIGHBOR_SOLICITATION => {
119 parse_neighbor_solicitation(&data[4..], &mut fields)
120 }
121 icmpv6_type::NEIGHBOR_ADVERTISEMENT => {
122 parse_neighbor_advertisement(&data[4..], &mut fields)
123 }
124 icmpv6_type::REDIRECT => parse_redirect(&data[4..], &mut fields),
125 icmpv6_type::MLD_QUERY | icmpv6_type::MLDV1_REPORT | icmpv6_type::MLDV1_DONE => {
127 parse_mldv1(&data[4..], &mut fields)
128 }
129 icmpv6_type::MLDV2_REPORT => parse_mldv2_report(&data[4..], &mut fields),
130 _ => 4, };
132
133 ParseResult::success(fields, &data[consumed..], SmallVec::new())
134 }
135
136 fn schema_fields(&self) -> Vec<FieldDescriptor> {
137 vec![
138 FieldDescriptor::new("icmpv6.type", DataKind::UInt8).set_nullable(true),
140 FieldDescriptor::new("icmpv6.code", DataKind::UInt8).set_nullable(true),
141 FieldDescriptor::new("icmpv6.checksum", DataKind::UInt16).set_nullable(true),
142 FieldDescriptor::new("icmpv6.type_name", DataKind::String).set_nullable(true),
143 FieldDescriptor::new("icmpv6.echo_id", DataKind::UInt16).set_nullable(true),
145 FieldDescriptor::new("icmpv6.echo_seq", DataKind::UInt16).set_nullable(true),
146 FieldDescriptor::new("icmpv6.mtu", DataKind::UInt32).set_nullable(true),
148 FieldDescriptor::new("icmpv6.pointer", DataKind::UInt32).set_nullable(true),
150 FieldDescriptor::new("icmpv6.ndp_target_address", DataKind::String).set_nullable(true),
152 FieldDescriptor::new("icmpv6.ndp_cur_hop_limit", DataKind::UInt8).set_nullable(true),
154 FieldDescriptor::new("icmpv6.ndp_managed_flag", DataKind::Bool).set_nullable(true),
155 FieldDescriptor::new("icmpv6.ndp_other_flag", DataKind::Bool).set_nullable(true),
156 FieldDescriptor::new("icmpv6.ndp_router_lifetime", DataKind::UInt16).set_nullable(true),
157 FieldDescriptor::new("icmpv6.ndp_reachable_time", DataKind::UInt32).set_nullable(true),
158 FieldDescriptor::new("icmpv6.ndp_retrans_timer", DataKind::UInt32).set_nullable(true),
159 FieldDescriptor::new("icmpv6.ndp_router_flag", DataKind::Bool).set_nullable(true),
161 FieldDescriptor::new("icmpv6.ndp_solicited_flag", DataKind::Bool).set_nullable(true),
162 FieldDescriptor::new("icmpv6.ndp_override_flag", DataKind::Bool).set_nullable(true),
163 FieldDescriptor::new("icmpv6.ndp_source_mac", DataKind::String).set_nullable(true),
165 FieldDescriptor::new("icmpv6.ndp_target_mac", DataKind::String).set_nullable(true),
166 FieldDescriptor::new("icmpv6.ndp_prefix", DataKind::String).set_nullable(true),
167 FieldDescriptor::new("icmpv6.ndp_prefix_length", DataKind::UInt8).set_nullable(true),
168 FieldDescriptor::new("icmpv6.mld_max_response_delay", DataKind::UInt16)
170 .set_nullable(true),
171 FieldDescriptor::new("icmpv6.mld_multicast_address", DataKind::String)
172 .set_nullable(true),
173 FieldDescriptor::new("icmpv6.mld_num_group_records", DataKind::UInt16)
174 .set_nullable(true),
175 ]
176 }
177
178 fn dependencies(&self) -> &'static [&'static str] {
179 &["ipv6"]
180 }
181}
182
183fn get_type_name(icmpv6_type: u8) -> &'static str {
185 match icmpv6_type {
186 icmpv6_type::DESTINATION_UNREACHABLE => "Destination Unreachable",
187 icmpv6_type::PACKET_TOO_BIG => "Packet Too Big",
188 icmpv6_type::TIME_EXCEEDED => "Time Exceeded",
189 icmpv6_type::PARAMETER_PROBLEM => "Parameter Problem",
190 icmpv6_type::ECHO_REQUEST => "Echo Request",
191 icmpv6_type::ECHO_REPLY => "Echo Reply",
192 icmpv6_type::MLD_QUERY => "MLD Query",
193 icmpv6_type::MLDV1_REPORT => "MLDv1 Report",
194 icmpv6_type::MLDV1_DONE => "MLDv1 Done",
195 icmpv6_type::MLDV2_REPORT => "MLDv2 Report",
196 icmpv6_type::ROUTER_SOLICITATION => "Router Solicitation",
197 icmpv6_type::ROUTER_ADVERTISEMENT => "Router Advertisement",
198 icmpv6_type::NEIGHBOR_SOLICITATION => "Neighbor Solicitation",
199 icmpv6_type::NEIGHBOR_ADVERTISEMENT => "Neighbor Advertisement",
200 icmpv6_type::REDIRECT => "Redirect",
201 _ => "Unknown",
202 }
203}
204
205fn parse_echo(data: &[u8], fields: &mut SmallVec<[(&'static str, FieldValue); 16]>) -> usize {
207 if data.len() < 4 {
208 return 4;
209 }
210 let id = u16::from_be_bytes([data[0], data[1]]);
211 let seq = u16::from_be_bytes([data[2], data[3]]);
212 fields.push(("echo_id", FieldValue::UInt16(id)));
213 fields.push(("echo_seq", FieldValue::UInt16(seq)));
214 8 }
216
217fn parse_packet_too_big(
219 data: &[u8],
220 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
221) -> usize {
222 if data.len() < 4 {
223 return 4;
224 }
225 let mtu = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
226 fields.push(("mtu", FieldValue::UInt32(mtu)));
227 8 }
229
230fn parse_parameter_problem(
232 data: &[u8],
233 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
234) -> usize {
235 if data.len() < 4 {
236 return 4;
237 }
238 let pointer = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
239 fields.push(("pointer", FieldValue::UInt32(pointer)));
240 8 }
242
243fn parse_router_solicitation(
245 data: &[u8],
246 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
247) -> usize {
248 if data.len() < 4 {
250 return 4;
251 }
252 let mut offset = 4; parse_ndp_options(&data[4..], fields);
254 offset += data.len().saturating_sub(4);
255 4 + offset.min(data.len())
256}
257
258fn parse_router_advertisement(
260 data: &[u8],
261 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
262) -> usize {
263 if data.len() < 12 {
265 return 4 + data.len();
266 }
267
268 let cur_hop_limit = data[0];
269 let flags = data[1];
270 let router_lifetime = u16::from_be_bytes([data[2], data[3]]);
271 let reachable_time = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
272 let retrans_timer = u32::from_be_bytes([data[8], data[9], data[10], data[11]]);
273
274 fields.push(("ndp_cur_hop_limit", FieldValue::UInt8(cur_hop_limit)));
275 fields.push(("ndp_managed_flag", FieldValue::Bool((flags & 0x80) != 0)));
276 fields.push(("ndp_other_flag", FieldValue::Bool((flags & 0x40) != 0)));
277 fields.push(("ndp_router_lifetime", FieldValue::UInt16(router_lifetime)));
278 fields.push(("ndp_reachable_time", FieldValue::UInt32(reachable_time)));
279 fields.push(("ndp_retrans_timer", FieldValue::UInt32(retrans_timer)));
280
281 if data.len() > 12 {
283 parse_ndp_options(&data[12..], fields);
284 }
285
286 4 + data.len()
287}
288
289fn parse_neighbor_solicitation(
291 data: &[u8],
292 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
293) -> usize {
294 if data.len() < 20 {
296 return 4 + data.len();
297 }
298
299 let target = format_ipv6(&data[4..20]);
301 fields.push((
302 "ndp_target_address",
303 FieldValue::OwnedString(CompactString::new(target)),
304 ));
305
306 if data.len() > 20 {
308 parse_ndp_options(&data[20..], fields);
309 }
310
311 4 + data.len()
312}
313
314fn parse_neighbor_advertisement(
316 data: &[u8],
317 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
318) -> usize {
319 if data.len() < 20 {
321 return 4 + data.len();
322 }
323
324 let flags = data[0];
325 fields.push(("ndp_router_flag", FieldValue::Bool((flags & 0x80) != 0)));
326 fields.push(("ndp_solicited_flag", FieldValue::Bool((flags & 0x40) != 0)));
327 fields.push(("ndp_override_flag", FieldValue::Bool((flags & 0x20) != 0)));
328
329 let target = format_ipv6(&data[4..20]);
330 fields.push((
331 "ndp_target_address",
332 FieldValue::OwnedString(CompactString::new(target)),
333 ));
334
335 if data.len() > 20 {
337 parse_ndp_options(&data[20..], fields);
338 }
339
340 4 + data.len()
341}
342
343fn parse_redirect(data: &[u8], fields: &mut SmallVec<[(&'static str, FieldValue); 16]>) -> usize {
345 if data.len() < 36 {
347 return 4 + data.len();
348 }
349
350 let target = format_ipv6(&data[4..20]);
352 fields.push((
353 "ndp_target_address",
354 FieldValue::OwnedString(CompactString::new(target)),
355 ));
356
357 if data.len() > 36 {
359 parse_ndp_options(&data[36..], fields);
360 }
361
362 4 + data.len()
363}
364
365fn parse_ndp_options(data: &[u8], fields: &mut SmallVec<[(&'static str, FieldValue); 16]>) {
367 let mut offset = 0;
368
369 while offset + 2 <= data.len() {
370 let opt_type = data[offset];
371 let opt_len = data[offset + 1] as usize * 8; if opt_len == 0 || offset + opt_len > data.len() {
374 break;
375 }
376
377 match opt_type {
378 ndp_option::SOURCE_LINK_LAYER_ADDR => {
379 if opt_len >= 8 {
380 let mac = format_mac(&data[offset + 2..offset + 8]);
381 fields.push((
382 "ndp_source_mac",
383 FieldValue::OwnedString(CompactString::new(mac)),
384 ));
385 }
386 }
387 ndp_option::TARGET_LINK_LAYER_ADDR => {
388 if opt_len >= 8 {
389 let mac = format_mac(&data[offset + 2..offset + 8]);
390 fields.push((
391 "ndp_target_mac",
392 FieldValue::OwnedString(CompactString::new(mac)),
393 ));
394 }
395 }
396 ndp_option::PREFIX_INFO => {
397 if opt_len >= 32 {
399 let prefix_len = data[offset + 2];
400 let prefix = format_ipv6(&data[offset + 16..offset + 32]);
401 fields.push(("ndp_prefix_length", FieldValue::UInt8(prefix_len)));
402 fields.push((
403 "ndp_prefix",
404 FieldValue::OwnedString(CompactString::new(prefix)),
405 ));
406 }
407 }
408 ndp_option::MTU => {
409 if opt_len >= 8 {
410 let mtu = u32::from_be_bytes([
411 data[offset + 4],
412 data[offset + 5],
413 data[offset + 6],
414 data[offset + 7],
415 ]);
416 fields.push(("mtu", FieldValue::UInt32(mtu)));
417 }
418 }
419 _ => {
420 }
422 }
423
424 offset += opt_len;
425 }
426}
427
428fn parse_mldv1(data: &[u8], fields: &mut SmallVec<[(&'static str, FieldValue); 16]>) -> usize {
430 if data.len() < 20 {
432 return 4 + data.len();
433 }
434
435 let max_response_delay = u16::from_be_bytes([data[0], data[1]]);
436 fields.push((
437 "mld_max_response_delay",
438 FieldValue::UInt16(max_response_delay),
439 ));
440
441 let multicast_addr = format_ipv6(&data[4..20]);
443 fields.push((
444 "mld_multicast_address",
445 FieldValue::OwnedString(CompactString::new(multicast_addr)),
446 ));
447
448 4 + 20 }
450
451fn parse_mldv2_report(
453 data: &[u8],
454 fields: &mut SmallVec<[(&'static str, FieldValue); 16]>,
455) -> usize {
456 if data.len() < 4 {
458 return 4 + data.len();
459 }
460
461 let num_group_records = u16::from_be_bytes([data[2], data[3]]);
463 fields.push((
464 "mld_num_group_records",
465 FieldValue::UInt16(num_group_records),
466 ));
467
468 4 + data.len() }
470
471fn format_ipv6(bytes: &[u8]) -> String {
473 if bytes.len() >= 16 {
474 let mut arr = [0u8; 16];
475 arr.copy_from_slice(&bytes[..16]);
476 Ipv6Addr::from(arr).to_string()
477 } else {
478 "::".to_string()
479 }
480}
481
482fn format_mac(bytes: &[u8]) -> String {
484 if bytes.len() >= 6 {
485 format!(
486 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
487 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
488 )
489 } else {
490 String::new()
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use super::*;
497
498 fn create_context_icmpv6() -> ParseContext {
499 let mut context = ParseContext::new(1);
500 context.insert_hint("ip_protocol", IP_PROTO_ICMPV6 as u64);
501 context.insert_hint("ip_version", 6);
502 context
503 }
504
505 #[test]
506 fn test_can_parse_with_icmpv6() {
507 let parser = Icmpv6Protocol;
508 let context = create_context_icmpv6();
509 assert!(parser.can_parse(&context).is_some());
510 }
511
512 #[test]
513 fn test_cannot_parse_without_ipv6() {
514 let parser = Icmpv6Protocol;
515 let mut context = ParseContext::new(1);
516 context.insert_hint("ip_protocol", IP_PROTO_ICMPV6 as u64);
517 context.insert_hint("ip_version", 4);
518 assert!(parser.can_parse(&context).is_none());
519 }
520
521 #[test]
522 fn test_cannot_parse_without_hint() {
523 let parser = Icmpv6Protocol;
524 let context = ParseContext::new(1);
525 assert!(parser.can_parse(&context).is_none());
526 }
527
528 #[test]
529 fn test_parse_echo_request() {
530 let parser = Icmpv6Protocol;
531 let context = create_context_icmpv6();
532
533 let data = [
535 0x80, 0x00, 0x12, 0x34, 0x00, 0x01, 0x00, 0x02, ];
541
542 let result = parser.parse(&data, &context);
543
544 assert!(result.is_ok());
545 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(128)));
546 assert_eq!(result.get("code"), Some(&FieldValue::UInt8(0)));
547 assert_eq!(result.get("echo_id"), Some(&FieldValue::UInt16(1)));
548 assert_eq!(result.get("echo_seq"), Some(&FieldValue::UInt16(2)));
549 assert_eq!(
550 result.get("type_name"),
551 Some(&FieldValue::Str("Echo Request"))
552 );
553 }
554
555 #[test]
556 fn test_parse_echo_reply() {
557 let parser = Icmpv6Protocol;
558 let context = create_context_icmpv6();
559
560 let data = [
561 0x81, 0x00, 0xab, 0xcd, 0x12, 0x34, 0x00, 0x0a, ];
567
568 let result = parser.parse(&data, &context);
569
570 assert!(result.is_ok());
571 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(129)));
572 assert_eq!(result.get("echo_id"), Some(&FieldValue::UInt16(0x1234)));
573 assert_eq!(result.get("echo_seq"), Some(&FieldValue::UInt16(10)));
574 assert_eq!(
575 result.get("type_name"),
576 Some(&FieldValue::Str("Echo Reply"))
577 );
578 }
579
580 #[test]
581 fn test_parse_destination_unreachable() {
582 let parser = Icmpv6Protocol;
583 let context = create_context_icmpv6();
584
585 let data = [
586 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
591
592 let result = parser.parse(&data, &context);
593
594 assert!(result.is_ok());
595 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(1)));
596 assert_eq!(result.get("code"), Some(&FieldValue::UInt8(4)));
597 assert_eq!(
598 result.get("type_name"),
599 Some(&FieldValue::Str("Destination Unreachable"))
600 );
601 }
602
603 #[test]
604 fn test_parse_packet_too_big() {
605 let parser = Icmpv6Protocol;
606 let context = create_context_icmpv6();
607
608 let data = [
609 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc, ];
614
615 let result = parser.parse(&data, &context);
616
617 assert!(result.is_ok());
618 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(2)));
619 assert_eq!(result.get("mtu"), Some(&FieldValue::UInt32(1500)));
620 assert_eq!(
621 result.get("type_name"),
622 Some(&FieldValue::Str("Packet Too Big"))
623 );
624 }
625
626 #[test]
627 fn test_parse_time_exceeded() {
628 let parser = Icmpv6Protocol;
629 let context = create_context_icmpv6();
630
631 let data = [
632 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
637
638 let result = parser.parse(&data, &context);
639
640 assert!(result.is_ok());
641 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(3)));
642 assert_eq!(
643 result.get("type_name"),
644 Some(&FieldValue::Str("Time Exceeded"))
645 );
646 }
647
648 #[test]
649 fn test_parse_parameter_problem() {
650 let parser = Icmpv6Protocol;
651 let context = create_context_icmpv6();
652
653 let data = [
654 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, ];
659
660 let result = parser.parse(&data, &context);
661
662 assert!(result.is_ok());
663 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(4)));
664 assert_eq!(result.get("code"), Some(&FieldValue::UInt8(2)));
665 assert_eq!(result.get("pointer"), Some(&FieldValue::UInt32(40)));
666 assert_eq!(
667 result.get("type_name"),
668 Some(&FieldValue::Str("Parameter Problem"))
669 );
670 }
671
672 #[test]
673 fn test_parse_too_short() {
674 let parser = Icmpv6Protocol;
675 let context = create_context_icmpv6();
676
677 let data = [0x80, 0x00]; let result = parser.parse(&data, &context);
680
681 assert!(!result.is_ok());
682 assert!(result.error.is_some());
683 }
684
685 #[test]
686 fn test_schema_fields() {
687 let parser = Icmpv6Protocol;
688 let fields = parser.schema_fields();
689
690 assert!(!fields.is_empty());
691 assert!(fields.iter().any(|f| f.name == "icmpv6.type"));
692 assert!(fields.iter().any(|f| f.name == "icmpv6.code"));
693 assert!(fields.iter().any(|f| f.name == "icmpv6.checksum"));
694 assert!(fields.iter().any(|f| f.name == "icmpv6.echo_id"));
695 assert!(fields.iter().any(|f| f.name == "icmpv6.mtu"));
696 }
697
698 #[test]
701 fn test_parse_router_solicitation() {
702 let parser = Icmpv6Protocol;
703 let context = create_context_icmpv6();
704
705 let data = vec![
707 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, ];
716
717 let result = parser.parse(&data, &context);
718
719 assert!(result.is_ok());
720 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(133)));
721 assert_eq!(
722 result.get("type_name"),
723 Some(&FieldValue::Str("Router Solicitation"))
724 );
725 match result.get("ndp_source_mac") {
727 Some(FieldValue::OwnedString(s)) if s.as_str() == "00:11:22:33:44:55" => {}
728 other => panic!(
729 "Expected OwnedString(\"00:11:22:33:44:55\"), got {:?}",
730 other
731 ),
732 }
733 }
734
735 #[test]
736 fn test_parse_router_advertisement() {
737 let parser = Icmpv6Protocol;
738 let context = create_context_icmpv6();
739
740 let data = [
741 0x86, 0x00, 0x00, 0x00, 0x40, 0xc0, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
750
751 let result = parser.parse(&data, &context);
752
753 assert!(result.is_ok());
754 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(134)));
755 assert_eq!(
756 result.get("ndp_cur_hop_limit"),
757 Some(&FieldValue::UInt8(64))
758 );
759 assert_eq!(
760 result.get("ndp_managed_flag"),
761 Some(&FieldValue::Bool(true))
762 );
763 assert_eq!(result.get("ndp_other_flag"), Some(&FieldValue::Bool(true)));
764 assert_eq!(
765 result.get("ndp_router_lifetime"),
766 Some(&FieldValue::UInt16(1800))
767 );
768 }
769
770 #[test]
771 fn test_parse_router_advertisement_flags() {
772 let parser = Icmpv6Protocol;
773 let context = create_context_icmpv6();
774
775 let data = [
777 0x86, 0x00, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
779 ];
780
781 let result = parser.parse(&data, &context);
782 assert_eq!(
783 result.get("ndp_managed_flag"),
784 Some(&FieldValue::Bool(true))
785 );
786 assert_eq!(result.get("ndp_other_flag"), Some(&FieldValue::Bool(false)));
787
788 let data2 = [
790 0x86, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
792 ];
793
794 let result2 = parser.parse(&data2, &context);
795 assert_eq!(
796 result2.get("ndp_managed_flag"),
797 Some(&FieldValue::Bool(false))
798 );
799 assert_eq!(result2.get("ndp_other_flag"), Some(&FieldValue::Bool(true)));
800 }
801
802 #[test]
803 fn test_parse_neighbor_solicitation() {
804 let parser = Icmpv6Protocol;
805 let context = create_context_icmpv6();
806
807 let data = [
809 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
815 0x00, 0x01,
816 ];
817
818 let result = parser.parse(&data, &context);
819
820 assert!(result.is_ok());
821 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(135)));
822 match result.get("ndp_target_address") {
823 Some(FieldValue::OwnedString(s)) if s.as_str() == "2001:db8::1" => {}
824 other => panic!("Expected OwnedString(\"2001:db8::1\"), got {:?}", other),
825 }
826 }
827
828 #[test]
829 fn test_parse_neighbor_advertisement() {
830 let parser = Icmpv6Protocol;
831 let context = create_context_icmpv6();
832
833 let data = [
835 0x88, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
841 0x00, 0x01,
842 ];
843
844 let result = parser.parse(&data, &context);
845
846 assert!(result.is_ok());
847 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(136)));
848 assert_eq!(result.get("ndp_router_flag"), Some(&FieldValue::Bool(true)));
849 assert_eq!(
850 result.get("ndp_solicited_flag"),
851 Some(&FieldValue::Bool(true))
852 );
853 assert_eq!(
854 result.get("ndp_override_flag"),
855 Some(&FieldValue::Bool(false))
856 );
857 match result.get("ndp_target_address") {
858 Some(FieldValue::OwnedString(s)) if s.as_str() == "2001:db8::1" => {}
859 other => panic!("Expected OwnedString(\"2001:db8::1\"), got {:?}", other),
860 }
861 }
862
863 #[test]
864 fn test_parse_neighbor_advertisement_flags() {
865 let parser = Icmpv6Protocol;
866 let context = create_context_icmpv6();
867
868 let data = [
870 0x88, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
872 0x00, 0x01,
873 ];
874
875 let result = parser.parse(&data, &context);
876 assert_eq!(
877 result.get("ndp_router_flag"),
878 Some(&FieldValue::Bool(false))
879 );
880 assert_eq!(
881 result.get("ndp_solicited_flag"),
882 Some(&FieldValue::Bool(false))
883 );
884 assert_eq!(
885 result.get("ndp_override_flag"),
886 Some(&FieldValue::Bool(true))
887 );
888 }
889
890 #[test]
891 fn test_parse_redirect() {
892 let parser = Icmpv6Protocol;
893 let context = create_context_icmpv6();
894
895 let mut data = vec![
896 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
901 data.extend_from_slice(&[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
903 data.extend_from_slice(&[0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
905
906 let result = parser.parse(&data, &context);
907
908 assert!(result.is_ok());
909 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(137)));
910 match result.get("ndp_target_address") {
911 Some(FieldValue::OwnedString(s)) if s.as_str() == "fe80::1" => {}
912 other => panic!("Expected OwnedString(\"fe80::1\"), got {:?}", other),
913 }
914 }
915
916 #[test]
917 fn test_parse_source_link_layer_option() {
918 let parser = Icmpv6Protocol;
919 let context = create_context_icmpv6();
920
921 let mut data = vec![
922 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
925 data.extend_from_slice(&[0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
927 data.extend_from_slice(&[0x01, 0x01, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
929
930 let result = parser.parse(&data, &context);
931
932 assert!(result.is_ok());
933 match result.get("ndp_source_mac") {
934 Some(FieldValue::OwnedString(s)) if s.as_str() == "aa:bb:cc:dd:ee:ff" => {}
935 other => panic!(
936 "Expected OwnedString(\"aa:bb:cc:dd:ee:ff\"), got {:?}",
937 other
938 ),
939 }
940 }
941
942 #[test]
943 fn test_parse_target_link_layer_option() {
944 let parser = Icmpv6Protocol;
945 let context = create_context_icmpv6();
946
947 let mut data = vec![
948 0x88, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, ];
951 data.extend_from_slice(&[0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
953 data.extend_from_slice(&[0x02, 0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]);
955
956 let result = parser.parse(&data, &context);
957
958 assert!(result.is_ok());
959 match result.get("ndp_target_mac") {
960 Some(FieldValue::OwnedString(s)) if s.as_str() == "11:22:33:44:55:66" => {}
961 other => panic!(
962 "Expected OwnedString(\"11:22:33:44:55:66\"), got {:?}",
963 other
964 ),
965 }
966 }
967
968 #[test]
969 fn test_parse_prefix_info_option() {
970 let parser = Icmpv6Protocol;
971 let context = create_context_icmpv6();
972
973 let mut data = vec![
974 0x86, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
980 data.extend_from_slice(&[
982 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
990 0x00, 0x00,
991 ]);
992
993 let result = parser.parse(&data, &context);
994
995 assert!(result.is_ok());
996 assert_eq!(
997 result.get("ndp_prefix_length"),
998 Some(&FieldValue::UInt8(64))
999 );
1000 match result.get("ndp_prefix") {
1001 Some(FieldValue::OwnedString(s)) if s.as_str() == "2001:db8::" => {}
1002 other => panic!("Expected OwnedString(\"2001:db8::\"), got {:?}", other),
1003 }
1004 }
1005
1006 #[test]
1009 fn test_parse_mld_query() {
1010 let parser = Icmpv6Protocol;
1011 let context = create_context_icmpv6();
1012
1013 let mut data = vec![
1014 0x82, 0x00, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, ];
1020 data.extend_from_slice(&[0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
1022
1023 let result = parser.parse(&data, &context);
1024
1025 assert!(result.is_ok());
1026 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(130)));
1027 assert_eq!(
1028 result.get("mld_max_response_delay"),
1029 Some(&FieldValue::UInt16(10000))
1030 );
1031 match result.get("mld_multicast_address") {
1032 Some(FieldValue::OwnedString(s)) if s.as_str() == "ff02::1" => {}
1033 other => panic!("Expected OwnedString(\"ff02::1\"), got {:?}", other),
1034 }
1035 }
1036
1037 #[test]
1038 fn test_parse_mldv1_report() {
1039 let parser = Icmpv6Protocol;
1040 let context = create_context_icmpv6();
1041
1042 let mut data = vec![
1043 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1049 data.extend_from_slice(&[0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12]);
1051
1052 let result = parser.parse(&data, &context);
1053
1054 assert!(result.is_ok());
1055 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(131)));
1056 assert_eq!(
1057 result.get("type_name"),
1058 Some(&FieldValue::Str("MLDv1 Report"))
1059 );
1060 }
1061
1062 #[test]
1063 fn test_parse_mldv1_done() {
1064 let parser = Icmpv6Protocol;
1065 let context = create_context_icmpv6();
1066
1067 let mut data = vec![
1068 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1070 ];
1071 data.extend_from_slice(&[0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12]);
1073
1074 let result = parser.parse(&data, &context);
1075
1076 assert!(result.is_ok());
1077 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(132)));
1078 assert_eq!(
1079 result.get("type_name"),
1080 Some(&FieldValue::Str("MLDv1 Done"))
1081 );
1082 }
1083
1084 #[test]
1085 fn test_parse_mldv2_report() {
1086 let parser = Icmpv6Protocol;
1087 let context = create_context_icmpv6();
1088
1089 let data = [
1090 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, ];
1096
1097 let result = parser.parse(&data, &context);
1098
1099 assert!(result.is_ok());
1100 assert_eq!(result.get("type"), Some(&FieldValue::UInt8(143)));
1101 assert_eq!(
1102 result.get("mld_num_group_records"),
1103 Some(&FieldValue::UInt16(3))
1104 );
1105 assert_eq!(
1106 result.get("type_name"),
1107 Some(&FieldValue::Str("MLDv2 Report"))
1108 );
1109 }
1110
1111 #[test]
1112 fn test_multicast_address_extraction() {
1113 let parser = Icmpv6Protocol;
1114 let context = create_context_icmpv6();
1115
1116 let mut data = vec![0x82, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00];
1118 data.extend_from_slice(&[0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x16]);
1119
1120 let result = parser.parse(&data, &context);
1121
1122 assert!(result.is_ok());
1123 match result.get("mld_multicast_address") {
1124 Some(FieldValue::OwnedString(s)) if s.as_str() == "ff02::16" => {}
1125 other => panic!("Expected OwnedString(\"ff02::16\"), got {:?}", other),
1126 }
1127 }
1128
1129 #[test]
1130 fn test_max_response_delay() {
1131 let parser = Icmpv6Protocol;
1132 let context = create_context_icmpv6();
1133
1134 let mut data = vec![
1135 0x82, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00,
1137 ];
1138 data.extend_from_slice(&[0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
1139
1140 let result = parser.parse(&data, &context);
1141
1142 assert!(result.is_ok());
1143 assert_eq!(
1144 result.get("mld_max_response_delay"),
1145 Some(&FieldValue::UInt16(1000))
1146 );
1147 }
1148}