1#![deny(missing_docs)]
17
18use packet_dissector_core::dissector::{DispatchHint, DissectResult, Dissector};
19use packet_dissector_core::error::PacketError;
20use packet_dissector_core::field::{FieldDescriptor, FieldType, FieldValue};
21use packet_dissector_core::packet::DissectBuffer;
22use packet_dissector_core::util::read_be_u32;
23
24const BFD_VERSION: u8 = 1;
27
28const MIN_HEADER_SIZE: usize = 24;
32
33const MIN_HEADER_SIZE_WITH_AUTH: usize = 26;
37
38const AUTH_LEN_MD5: usize = 24;
42
43const AUTH_LEN_SHA1: usize = 28;
47
48const MIN_AUTH_LEN_SIMPLE_PASSWORD: usize = 4;
52
53const MIN_AUTH_LEN_UNKNOWN: usize = 3;
56
57fn diagnostic_name(diag: u8) -> &'static str {
72 match diag {
73 0 => "No Diagnostic",
74 1 => "Control Detection Time Expired",
75 2 => "Echo Function Failed",
76 3 => "Neighbor Signaled Session Down",
77 4 => "Forwarding Plane Reset",
78 5 => "Path Down",
79 6 => "Concatenated Path Down",
80 7 => "Administratively Down",
81 8 => "Reverse Concatenated Path Down",
82 _ => "Reserved",
83 }
84}
85
86fn state_name(state: u8) -> &'static str {
95 match state {
96 0 => "AdminDown",
97 1 => "Down",
98 2 => "Init",
99 3 => "Up",
100 _ => unreachable!(),
102 }
103}
104
105fn auth_type_name(auth_type: u8) -> &'static str {
117 match auth_type {
118 0 => "Reserved",
119 1 => "Simple Password",
120 2 => "Keyed MD5",
121 3 => "Meticulous Keyed MD5",
122 4 => "Keyed SHA1",
123 5 => "Meticulous Keyed SHA1",
124 _ => "Reserved",
125 }
126}
127
128pub struct BfdDissector;
130
131static FIELD_DESCRIPTORS: &[FieldDescriptor] = &[
133 FieldDescriptor::new("version", "Version", FieldType::U8),
134 FieldDescriptor {
135 name: "diagnostic",
136 display_name: "Diagnostic",
137 field_type: FieldType::U8,
138 optional: false,
139 children: None,
140 display_fn: Some(|v, _siblings| match v {
141 FieldValue::U8(d) => Some(diagnostic_name(*d)),
142 _ => None,
143 }),
144 format_fn: None,
145 },
146 FieldDescriptor {
147 name: "state",
148 display_name: "State",
149 field_type: FieldType::U8,
150 optional: false,
151 children: None,
152 display_fn: Some(|v, _siblings| match v {
153 FieldValue::U8(s) => Some(state_name(*s)),
154 _ => None,
155 }),
156 format_fn: None,
157 },
158 FieldDescriptor::new("poll", "Poll", FieldType::U8),
159 FieldDescriptor::new("final", "Final", FieldType::U8),
160 FieldDescriptor::new(
161 "control_plane_independent",
162 "Control Plane Independent",
163 FieldType::U8,
164 ),
165 FieldDescriptor::new("auth_present", "Authentication Present", FieldType::U8),
166 FieldDescriptor::new("demand", "Demand", FieldType::U8),
167 FieldDescriptor::new("multipoint", "Multipoint", FieldType::U8),
168 FieldDescriptor::new("detect_mult", "Detect Multiplier", FieldType::U8),
169 FieldDescriptor::new("length", "Length", FieldType::U8),
170 FieldDescriptor::new("my_discriminator", "My Discriminator", FieldType::U32),
171 FieldDescriptor::new("your_discriminator", "Your Discriminator", FieldType::U32),
172 FieldDescriptor::new(
173 "desired_min_tx_interval",
174 "Desired Min TX Interval",
175 FieldType::U32,
176 ),
177 FieldDescriptor::new(
178 "required_min_rx_interval",
179 "Required Min RX Interval",
180 FieldType::U32,
181 ),
182 FieldDescriptor::new(
183 "required_min_echo_rx_interval",
184 "Required Min Echo RX Interval",
185 FieldType::U32,
186 ),
187 FieldDescriptor {
189 name: "auth_type",
190 display_name: "Auth Type",
191 field_type: FieldType::U8,
192 optional: true,
193 children: None,
194 display_fn: Some(|v, _siblings| match v {
195 FieldValue::U8(a) => Some(auth_type_name(*a)),
196 _ => None,
197 }),
198 format_fn: None,
199 },
200 FieldDescriptor::new("auth_data", "Auth Data", FieldType::Bytes).optional(),
201];
202
203const FD_VERSION: usize = 0;
205const FD_DIAGNOSTIC: usize = 1;
206const FD_STATE: usize = 2;
207const FD_POLL: usize = 3;
208const FD_FINAL: usize = 4;
209const FD_CONTROL_PLANE_INDEPENDENT: usize = 5;
210const FD_AUTH_PRESENT: usize = 6;
211const FD_DEMAND: usize = 7;
212const FD_MULTIPOINT: usize = 8;
213const FD_DETECT_MULT: usize = 9;
214const FD_LENGTH: usize = 10;
215const FD_MY_DISCRIMINATOR: usize = 11;
216const FD_YOUR_DISCRIMINATOR: usize = 12;
217const FD_DESIRED_MIN_TX_INTERVAL: usize = 13;
218const FD_REQUIRED_MIN_RX_INTERVAL: usize = 14;
219const FD_REQUIRED_MIN_ECHO_RX_INTERVAL: usize = 15;
220const FD_AUTH_TYPE: usize = 16;
222const FD_AUTH_DATA: usize = 17;
223
224impl Dissector for BfdDissector {
225 fn name(&self) -> &'static str {
226 "Bidirectional Forwarding Detection"
227 }
228
229 fn short_name(&self) -> &'static str {
230 "BFD"
231 }
232
233 fn field_descriptors(&self) -> &'static [FieldDescriptor] {
234 FIELD_DESCRIPTORS
235 }
236
237 fn dissect<'pkt>(
238 &self,
239 data: &'pkt [u8],
240 buf: &mut DissectBuffer<'pkt>,
241 offset: usize,
242 ) -> Result<DissectResult, PacketError> {
243 if data.len() < MIN_HEADER_SIZE {
244 return Err(PacketError::Truncated {
245 expected: MIN_HEADER_SIZE,
246 actual: data.len(),
247 });
248 }
249
250 let byte0 = data[0];
254 let version = (byte0 >> 5) & 0x07;
255 let diagnostic = byte0 & 0x1F;
256
257 if version != BFD_VERSION {
261 return Err(PacketError::InvalidFieldValue {
262 field: "version",
263 value: u32::from(version),
264 });
265 }
266
267 let byte1 = data[1];
271 let state = (byte1 >> 6) & 0x03;
272 let poll = (byte1 >> 5) & 0x01;
273 let final_flag = (byte1 >> 4) & 0x01;
274 let control_plane_independent = (byte1 >> 3) & 0x01;
275 let auth_present = (byte1 >> 2) & 0x01;
276 let demand = (byte1 >> 1) & 0x01;
277 let multipoint = byte1 & 0x01;
282
283 let detect_mult = data[2];
284 if detect_mult == 0 {
288 return Err(PacketError::InvalidFieldValue {
289 field: "detect_mult",
290 value: 0,
291 });
292 }
293
294 let length_u8 = data[3];
298 let length = length_u8 as usize;
299 if length < MIN_HEADER_SIZE {
305 return Err(PacketError::InvalidFieldValue {
306 field: "length",
307 value: length_u8 as u32,
308 });
309 }
310 if data.len() < length {
315 return Err(PacketError::Truncated {
316 expected: length,
317 actual: data.len(),
318 });
319 }
320
321 let my_discriminator = read_be_u32(data, 4)?;
322 if my_discriminator == 0 {
326 return Err(PacketError::InvalidFieldValue {
327 field: "my_discriminator",
328 value: 0,
329 });
330 }
331 let your_discriminator = read_be_u32(data, 8)?;
332 let desired_min_tx = read_be_u32(data, 12)?;
333 let required_min_rx = read_be_u32(data, 16)?;
334 let required_min_echo_rx = read_be_u32(data, 20)?;
335
336 buf.begin_layer(
337 self.short_name(),
338 None,
339 FIELD_DESCRIPTORS,
340 offset..offset + length,
341 );
342 buf.push_field(
343 &FIELD_DESCRIPTORS[FD_VERSION],
344 FieldValue::U8(version),
345 offset..offset + 1,
346 );
347 buf.push_field(
348 &FIELD_DESCRIPTORS[FD_DIAGNOSTIC],
349 FieldValue::U8(diagnostic),
350 offset..offset + 1,
351 );
352 buf.push_field(
353 &FIELD_DESCRIPTORS[FD_STATE],
354 FieldValue::U8(state),
355 offset + 1..offset + 2,
356 );
357 buf.push_field(
358 &FIELD_DESCRIPTORS[FD_POLL],
359 FieldValue::U8(poll),
360 offset + 1..offset + 2,
361 );
362 buf.push_field(
363 &FIELD_DESCRIPTORS[FD_FINAL],
364 FieldValue::U8(final_flag),
365 offset + 1..offset + 2,
366 );
367 buf.push_field(
368 &FIELD_DESCRIPTORS[FD_CONTROL_PLANE_INDEPENDENT],
369 FieldValue::U8(control_plane_independent),
370 offset + 1..offset + 2,
371 );
372 buf.push_field(
373 &FIELD_DESCRIPTORS[FD_AUTH_PRESENT],
374 FieldValue::U8(auth_present),
375 offset + 1..offset + 2,
376 );
377 buf.push_field(
378 &FIELD_DESCRIPTORS[FD_DEMAND],
379 FieldValue::U8(demand),
380 offset + 1..offset + 2,
381 );
382 buf.push_field(
383 &FIELD_DESCRIPTORS[FD_MULTIPOINT],
384 FieldValue::U8(multipoint),
385 offset + 1..offset + 2,
386 );
387 buf.push_field(
388 &FIELD_DESCRIPTORS[FD_DETECT_MULT],
389 FieldValue::U8(detect_mult),
390 offset + 2..offset + 3,
391 );
392 buf.push_field(
393 &FIELD_DESCRIPTORS[FD_LENGTH],
394 FieldValue::U8(length_u8),
395 offset + 3..offset + 4,
396 );
397 buf.push_field(
398 &FIELD_DESCRIPTORS[FD_MY_DISCRIMINATOR],
399 FieldValue::U32(my_discriminator),
400 offset + 4..offset + 8,
401 );
402 buf.push_field(
403 &FIELD_DESCRIPTORS[FD_YOUR_DISCRIMINATOR],
404 FieldValue::U32(your_discriminator),
405 offset + 8..offset + 12,
406 );
407 buf.push_field(
408 &FIELD_DESCRIPTORS[FD_DESIRED_MIN_TX_INTERVAL],
409 FieldValue::U32(desired_min_tx),
410 offset + 12..offset + 16,
411 );
412 buf.push_field(
413 &FIELD_DESCRIPTORS[FD_REQUIRED_MIN_RX_INTERVAL],
414 FieldValue::U32(required_min_rx),
415 offset + 16..offset + 20,
416 );
417 buf.push_field(
418 &FIELD_DESCRIPTORS[FD_REQUIRED_MIN_ECHO_RX_INTERVAL],
419 FieldValue::U32(required_min_echo_rx),
420 offset + 20..offset + 24,
421 );
422
423 if auth_present == 1 {
427 if length < MIN_HEADER_SIZE_WITH_AUTH {
431 return Err(PacketError::InvalidHeader(
432 "BFD auth present but length is less than minimum with auth",
433 ));
434 }
435 let auth_type = data[24];
436 let auth_len = data[25] as usize;
437
438 let min_auth_len = match auth_type {
453 1 => MIN_AUTH_LEN_SIMPLE_PASSWORD,
454 2 | 3 => AUTH_LEN_MD5,
455 4 | 5 => AUTH_LEN_SHA1,
456 _ => MIN_AUTH_LEN_UNKNOWN,
457 };
458
459 if auth_len < min_auth_len {
460 return Err(PacketError::InvalidHeader(
461 "BFD auth length is less than minimum for auth type",
462 ));
463 }
464 let auth_end = 24 + auth_len;
465 if auth_end > length {
466 return Err(PacketError::InvalidHeader(
467 "BFD auth section exceeds packet length",
468 ));
469 }
470
471 buf.push_field(
472 &FIELD_DESCRIPTORS[FD_AUTH_TYPE],
473 FieldValue::U8(auth_type),
474 offset + 24..offset + 25,
475 );
476 if auth_len > 2 {
477 buf.push_field(
478 &FIELD_DESCRIPTORS[FD_AUTH_DATA],
479 FieldValue::Bytes(&data[26..auth_end]),
480 offset + 26..offset + auth_end,
481 );
482 }
483 }
484
485 buf.end_layer();
486
487 Ok(DissectResult::new(length, DispatchHint::End))
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 #[allow(clippy::too_many_arguments)]
526 fn build_bfd(
527 version: u8,
528 diagnostic: u8,
529 state: u8,
530 poll: u8,
531 final_f: u8,
532 cpi: u8,
533 auth: u8,
534 demand: u8,
535 multipoint: u8,
536 detect_mult: u8,
537 length: u8,
538 my_disc: u32,
539 your_disc: u32,
540 desired_min_tx: u32,
541 required_min_rx: u32,
542 required_min_echo_rx: u32,
543 ) -> Vec<u8> {
544 let byte0 = (version << 5) | (diagnostic & 0x1F);
545 let byte1 = (state << 6)
546 | (poll << 5)
547 | (final_f << 4)
548 | (cpi << 3)
549 | (auth << 2)
550 | (demand << 1)
551 | multipoint;
552 let mut pkt = Vec::with_capacity(length as usize);
553 pkt.push(byte0);
554 pkt.push(byte1);
555 pkt.push(detect_mult);
556 pkt.push(length);
557 pkt.extend_from_slice(&my_disc.to_be_bytes());
558 pkt.extend_from_slice(&your_disc.to_be_bytes());
559 pkt.extend_from_slice(&desired_min_tx.to_be_bytes());
560 pkt.extend_from_slice(&required_min_rx.to_be_bytes());
561 pkt.extend_from_slice(&required_min_echo_rx.to_be_bytes());
562 pkt
563 }
564
565 #[allow(clippy::too_many_arguments)]
567 fn build_bfd_with_auth(
568 version: u8,
569 diagnostic: u8,
570 state: u8,
571 detect_mult: u8,
572 my_disc: u32,
573 your_disc: u32,
574 auth_type: u8,
575 auth_data: &[u8],
576 ) -> Vec<u8> {
577 let auth_len = 2 + auth_data.len();
578 let total_len = 24 + auth_len;
579 let mut pkt = build_bfd(
580 version,
581 diagnostic,
582 state,
583 0,
584 0,
585 0,
586 1, 0,
588 0,
589 detect_mult,
590 total_len as u8,
591 my_disc,
592 your_disc,
593 1_000_000,
594 1_000_000,
595 0,
596 );
597 pkt.push(auth_type);
598 pkt.push(auth_len as u8);
599 pkt.extend_from_slice(auth_data);
600 pkt
601 }
602
603 #[test]
604 fn test_parse_basic_up() {
605 let data = build_bfd(
607 1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 0x0001, 0x0002, 1_000_000, 1_000_000, 0, );
624 let mut buf = DissectBuffer::new();
625 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
626
627 assert_eq!(buf.layers().len(), 1);
628 let layer = &buf.layers()[0];
629 assert_eq!(layer.name, "BFD");
630
631 assert_eq!(
632 buf.field_by_name(layer, "version").unwrap().value,
633 FieldValue::U8(1)
634 );
635 assert_eq!(
636 buf.field_by_name(layer, "diagnostic").unwrap().value,
637 FieldValue::U8(0)
638 );
639 assert_eq!(
640 buf.resolve_display_name(layer, "diagnostic_name"),
641 Some("No Diagnostic")
642 );
643 assert_eq!(
644 buf.field_by_name(layer, "state").unwrap().value,
645 FieldValue::U8(3)
646 );
647 assert_eq!(buf.resolve_display_name(layer, "state_name"), Some("Up"));
648 assert_eq!(
649 buf.field_by_name(layer, "poll").unwrap().value,
650 FieldValue::U8(0)
651 );
652 assert_eq!(
653 buf.field_by_name(layer, "final").unwrap().value,
654 FieldValue::U8(0)
655 );
656 assert_eq!(
657 buf.field_by_name(layer, "control_plane_independent")
658 .unwrap()
659 .value,
660 FieldValue::U8(0)
661 );
662 assert_eq!(
663 buf.field_by_name(layer, "auth_present").unwrap().value,
664 FieldValue::U8(0)
665 );
666 assert_eq!(
667 buf.field_by_name(layer, "demand").unwrap().value,
668 FieldValue::U8(0)
669 );
670 assert_eq!(
671 buf.field_by_name(layer, "multipoint").unwrap().value,
672 FieldValue::U8(0)
673 );
674 assert_eq!(
675 buf.field_by_name(layer, "detect_mult").unwrap().value,
676 FieldValue::U8(3)
677 );
678 assert_eq!(
679 buf.field_by_name(layer, "length").unwrap().value,
680 FieldValue::U8(24)
681 );
682 assert_eq!(
683 buf.field_by_name(layer, "my_discriminator").unwrap().value,
684 FieldValue::U32(0x0001)
685 );
686 assert_eq!(
687 buf.field_by_name(layer, "your_discriminator")
688 .unwrap()
689 .value,
690 FieldValue::U32(0x0002)
691 );
692 assert_eq!(
693 buf.field_by_name(layer, "desired_min_tx_interval")
694 .unwrap()
695 .value,
696 FieldValue::U32(1_000_000)
697 );
698 assert_eq!(
699 buf.field_by_name(layer, "required_min_rx_interval")
700 .unwrap()
701 .value,
702 FieldValue::U32(1_000_000)
703 );
704 assert_eq!(
705 buf.field_by_name(layer, "required_min_echo_rx_interval")
706 .unwrap()
707 .value,
708 FieldValue::U32(0)
709 );
710 }
711
712 #[test]
713 fn test_parse_all_flags_set() {
714 let mut data = build_bfd(
717 1, 7, 0, 1, 1, 1, 1, 1, 1, 5, 28, 0xAABBCCDD, 0x11223344, 500_000, 500_000, 100_000,
727 );
728 data.push(1); data.push(4); data.push(0x41); data.push(0x42); let mut buf = DissectBuffer::new();
735 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
736
737 let layer = &buf.layers()[0];
738 assert_eq!(
739 buf.field_by_name(layer, "poll").unwrap().value,
740 FieldValue::U8(1)
741 );
742 assert_eq!(
743 buf.field_by_name(layer, "final").unwrap().value,
744 FieldValue::U8(1)
745 );
746 assert_eq!(
747 buf.field_by_name(layer, "control_plane_independent")
748 .unwrap()
749 .value,
750 FieldValue::U8(1)
751 );
752 assert_eq!(
753 buf.field_by_name(layer, "auth_present").unwrap().value,
754 FieldValue::U8(1)
755 );
756 assert_eq!(
757 buf.field_by_name(layer, "demand").unwrap().value,
758 FieldValue::U8(1)
759 );
760 assert_eq!(
761 buf.field_by_name(layer, "multipoint").unwrap().value,
762 FieldValue::U8(1)
763 );
764 assert_eq!(
765 buf.field_by_name(layer, "auth_type").unwrap().value,
766 FieldValue::U8(1)
767 );
768 assert_eq!(
769 buf.resolve_display_name(layer, "auth_type_name"),
770 Some("Simple Password")
771 );
772 assert_eq!(
773 buf.field_by_name(layer, "auth_data").unwrap().value,
774 FieldValue::Bytes(&[0x41, 0x42])
775 );
776 }
777
778 #[test]
779 fn test_diagnostic_codes() {
780 for diag in 0..=8 {
781 let data = build_bfd(
782 1, diag, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
783 );
784 let mut buf = DissectBuffer::new();
785 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
786 let layer = &buf.layers()[0];
787 assert_eq!(
788 buf.field_by_name(layer, "diagnostic").unwrap().value,
789 FieldValue::U8(diag)
790 );
791 if let Some(name) = buf.resolve_display_name(layer, "diagnostic_name") {
792 assert!(!name.is_empty());
793 assert_ne!(name, "Reserved");
794 } else {
795 panic!("diagnostic_name should be Str");
796 }
797 }
798 let data = build_bfd(
800 1, 9, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
801 );
802 let mut buf = DissectBuffer::new();
803 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
804 assert_eq!(
805 buf.resolve_display_name(&buf.layers()[0], "diagnostic_name"),
806 Some("Reserved")
807 );
808 }
809
810 #[test]
811 fn test_state_values() {
812 let names = ["AdminDown", "Down", "Init", "Up"];
813 for state in 0..=3u8 {
814 let data = build_bfd(
815 1, 0, state, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
816 );
817 let mut buf = DissectBuffer::new();
818 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
819 let layer = &buf.layers()[0];
820 assert_eq!(
821 buf.field_by_name(layer, "state").unwrap().value,
822 FieldValue::U8(state)
823 );
824 assert_eq!(
825 buf.resolve_display_name(layer, "state_name"),
826 Some(names[state as usize])
827 );
828 }
829 }
830
831 #[test]
832 fn test_parse_with_auth_simple() {
833 let auth_data = [1, b's', b'e', b'c', b'r', b'e', b't']; let data = build_bfd_with_auth(1, 0, 3, 3, 0x1000, 0x2000, 1, &auth_data);
836 let mut buf = DissectBuffer::new();
837 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
838
839 let layer = &buf.layers()[0];
840 assert_eq!(
841 buf.field_by_name(layer, "auth_present").unwrap().value,
842 FieldValue::U8(1)
843 );
844 assert_eq!(
845 buf.field_by_name(layer, "auth_type").unwrap().value,
846 FieldValue::U8(1)
847 );
848 assert_eq!(
849 buf.resolve_display_name(layer, "auth_type_name"),
850 Some("Simple Password")
851 );
852 assert_eq!(
853 buf.field_by_name(layer, "auth_data").unwrap().value,
854 FieldValue::Bytes(&auth_data)
855 );
856 }
857
858 #[test]
859 fn test_parse_with_auth_sha1() {
860 let mut auth_data = vec![1, 0, 0, 0]; auth_data.extend_from_slice(&1u32.to_be_bytes()); auth_data.extend_from_slice(&[0xAA; 20]); let data = build_bfd_with_auth(1, 0, 3, 3, 0x1000, 0x2000, 4, &auth_data);
865 let mut buf = DissectBuffer::new();
866 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
867
868 let layer = &buf.layers()[0];
869 assert_eq!(
870 buf.field_by_name(layer, "auth_type").unwrap().value,
871 FieldValue::U8(4)
872 );
873 assert_eq!(
874 buf.resolve_display_name(layer, "auth_type_name"),
875 Some("Keyed SHA1")
876 );
877 }
878
879 #[test]
880 fn test_truncated_packet() {
881 let data = [0u8; 23]; let mut buf = DissectBuffer::new();
883 let result = BfdDissector.dissect(&data, &mut buf, 0);
884 assert!(result.is_err());
885 match result.unwrap_err() {
886 PacketError::Truncated { expected, actual } => {
887 assert_eq!(expected, 24);
888 assert_eq!(actual, 23);
889 }
890 other => panic!("Expected Truncated, got {other:?}"),
891 }
892 }
893
894 #[test]
895 fn test_invalid_length_field() {
896 let data = build_bfd(
898 1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 20, 1, 0, 1_000_000, 1_000_000, 0,
899 );
900 let mut buf = DissectBuffer::new();
901 let result = BfdDissector.dissect(&data, &mut buf, 0);
902 assert!(result.is_err());
903 match result.unwrap_err() {
904 PacketError::InvalidFieldValue { field, value } => {
905 assert_eq!(field, "length");
906 assert_eq!(value, 20);
907 }
908 other => panic!("Expected InvalidFieldValue, got {other:?}"),
909 }
910 }
911
912 #[test]
913 fn test_auth_present_but_truncated() {
914 let data = build_bfd(
916 1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
917 );
918 let mut buf = DissectBuffer::new();
919 let result = BfdDissector.dissect(&data, &mut buf, 0);
920 assert!(result.is_err());
921 match result.unwrap_err() {
922 PacketError::InvalidHeader(msg) => {
923 assert!(msg.contains("auth present"));
924 }
925 other => panic!("Expected InvalidHeader, got {other:?}"),
926 }
927 }
928
929 #[test]
930 fn test_dissect_with_offset() {
931 let data = build_bfd(
932 1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
933 );
934 let offset = 42; let mut buf = DissectBuffer::new();
936 BfdDissector.dissect(&data, &mut buf, offset).unwrap();
937
938 let layer = &buf.layers()[0];
939 assert_eq!(layer.range, offset..offset + 24);
940 assert_eq!(
941 buf.field_by_name(layer, "required_min_echo_rx_interval")
942 .unwrap()
943 .range,
944 offset + 20..offset + 24
945 );
946 }
947
948 #[test]
949 fn test_field_descriptors() {
950 let descriptors = BfdDissector.field_descriptors();
951 assert_eq!(descriptors.len(), 18);
952 assert_eq!(descriptors[0].name, "version");
953 assert_eq!(descriptors[descriptors.len() - 1].name, "auth_data");
954 assert!(!descriptors[0].optional); assert!(descriptors[16].optional); assert!(descriptors[17].optional); }
959
960 #[test]
961 fn test_length_exceeds_data() {
962 let data = build_bfd(
964 1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 30, 1, 0, 1_000_000, 1_000_000, 0,
965 );
966 let mut buf = DissectBuffer::new();
967 let result = BfdDissector.dissect(&data, &mut buf, 0);
968 assert!(result.is_err());
969 match result.unwrap_err() {
970 PacketError::Truncated { expected, actual } => {
971 assert_eq!(expected, 30);
972 assert_eq!(actual, 24);
973 }
974 other => panic!("Expected Truncated, got {other:?}"),
975 }
976 }
977
978 #[test]
979 fn test_invalid_version() {
980 let data = build_bfd(
983 0, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
984 );
985 let mut buf = DissectBuffer::new();
986 let result = BfdDissector.dissect(&data, &mut buf, 0);
987 match result.unwrap_err() {
988 PacketError::InvalidFieldValue { field, value } => {
989 assert_eq!(field, "version");
990 assert_eq!(value, 0);
991 }
992 other => panic!("Expected InvalidFieldValue, got {other:?}"),
993 }
994
995 let data = build_bfd(
997 2, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
998 );
999 let mut buf = DissectBuffer::new();
1000 let result = BfdDissector.dissect(&data, &mut buf, 0);
1001 match result.unwrap_err() {
1002 PacketError::InvalidFieldValue { field, value } => {
1003 assert_eq!(field, "version");
1004 assert_eq!(value, 2);
1005 }
1006 other => panic!("Expected InvalidFieldValue, got {other:?}"),
1007 }
1008 }
1009
1010 #[test]
1011 fn test_detect_mult_zero() {
1012 let data = build_bfd(
1015 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 24, 1, 0, 1_000_000, 1_000_000, 0,
1016 );
1017 let mut buf = DissectBuffer::new();
1018 let result = BfdDissector.dissect(&data, &mut buf, 0);
1019 match result.unwrap_err() {
1020 PacketError::InvalidFieldValue { field, value } => {
1021 assert_eq!(field, "detect_mult");
1022 assert_eq!(value, 0);
1023 }
1024 other => panic!("Expected InvalidFieldValue, got {other:?}"),
1025 }
1026 }
1027
1028 #[test]
1029 fn test_my_discriminator_zero() {
1030 let data = build_bfd(
1033 1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 0, 0, 1_000_000, 1_000_000, 0,
1034 );
1035 let mut buf = DissectBuffer::new();
1036 let result = BfdDissector.dissect(&data, &mut buf, 0);
1037 match result.unwrap_err() {
1038 PacketError::InvalidFieldValue { field, value } => {
1039 assert_eq!(field, "my_discriminator");
1040 assert_eq!(value, 0);
1041 }
1042 other => panic!("Expected InvalidFieldValue, got {other:?}"),
1043 }
1044 }
1045
1046 #[test]
1047 fn test_parse_multipoint_bit_set() {
1048 let data = build_bfd(
1052 1, 0, 3, 0, 0, 0, 0, 0, 1, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
1053 );
1054 let mut buf = DissectBuffer::new();
1055 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
1056 let layer = &buf.layers()[0];
1057 assert_eq!(
1058 buf.field_by_name(layer, "multipoint").unwrap().value,
1059 FieldValue::U8(1)
1060 );
1061 }
1062
1063 #[test]
1064 fn test_simple_password_too_short() {
1065 let mut pkt = build_bfd(
1069 1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 27, 1, 0, 1_000_000, 1_000_000, 0,
1070 );
1071 pkt.push(1); pkt.push(3); pkt.push(1); let mut buf = DissectBuffer::new();
1075 let result = BfdDissector.dissect(&pkt, &mut buf, 0);
1076 match result.unwrap_err() {
1077 PacketError::InvalidHeader(msg) => {
1078 assert!(msg.contains("auth length"), "unexpected message: {msg}");
1079 }
1080 other => panic!("Expected InvalidHeader, got {other:?}"),
1081 }
1082 }
1083
1084 #[test]
1085 fn test_parse_with_auth_md5() {
1086 let mut auth_data = vec![1, 0]; auth_data.extend_from_slice(&42u32.to_be_bytes()); auth_data.extend_from_slice(&[0xBB; 16]); let data = build_bfd_with_auth(1, 0, 3, 3, 0x1000, 0x2000, 2, &auth_data);
1092 let mut buf = DissectBuffer::new();
1093 BfdDissector.dissect(&data, &mut buf, 0).unwrap();
1094
1095 let layer = &buf.layers()[0];
1096 assert_eq!(
1097 buf.field_by_name(layer, "auth_type").unwrap().value,
1098 FieldValue::U8(2)
1099 );
1100 assert_eq!(
1101 buf.resolve_display_name(layer, "auth_type_name"),
1102 Some("Keyed MD5")
1103 );
1104 }
1105
1106 #[test]
1107 fn test_md5_wrong_length() {
1108 let mut pkt = build_bfd(
1110 1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 44, 1, 0, 1_000_000, 1_000_000, 0,
1111 );
1112 pkt.push(2); pkt.push(20); pkt.extend_from_slice(&[0u8; 18]); let mut buf = DissectBuffer::new();
1116 let result = BfdDissector.dissect(&pkt, &mut buf, 0);
1117 match result.unwrap_err() {
1118 PacketError::InvalidHeader(msg) => {
1119 assert!(msg.contains("auth length"), "unexpected message: {msg}");
1120 }
1121 other => panic!("Expected InvalidHeader, got {other:?}"),
1122 }
1123 }
1124
1125 #[test]
1126 fn test_sha1_wrong_length() {
1127 let mut pkt = build_bfd(
1129 1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 48, 1, 0, 1_000_000, 1_000_000, 0,
1130 );
1131 pkt.push(4); pkt.push(24); pkt.extend_from_slice(&[0u8; 22]); let mut buf = DissectBuffer::new();
1135 let result = BfdDissector.dissect(&pkt, &mut buf, 0);
1136 match result.unwrap_err() {
1137 PacketError::InvalidHeader(msg) => {
1138 assert!(msg.contains("auth length"), "unexpected message: {msg}");
1139 }
1140 other => panic!("Expected InvalidHeader, got {other:?}"),
1141 }
1142 }
1143
1144 #[test]
1145 fn test_auth_section_exceeds_length() {
1146 let mut pkt = build_bfd(
1148 1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 28, 1, 0, 1_000_000, 1_000_000, 0,
1149 );
1150 pkt.push(1); pkt.push(10); pkt.push(1);
1153 pkt.push(b'x');
1154 let mut buf = DissectBuffer::new();
1155 let result = BfdDissector.dissect(&pkt, &mut buf, 0);
1156 match result.unwrap_err() {
1157 PacketError::InvalidHeader(msg) => {
1158 assert!(msg.contains("exceeds"), "unexpected message: {msg}");
1159 }
1160 other => panic!("Expected InvalidHeader, got {other:?}"),
1161 }
1162 }
1163}