#![deny(missing_docs)]
use packet_dissector_core::dissector::{DispatchHint, DissectResult, Dissector};
use packet_dissector_core::error::PacketError;
use packet_dissector_core::field::{FieldDescriptor, FieldType, FieldValue};
use packet_dissector_core::packet::DissectBuffer;
use packet_dissector_core::util::read_be_u32;
const BFD_VERSION: u8 = 1;
const MIN_HEADER_SIZE: usize = 24;
const MIN_HEADER_SIZE_WITH_AUTH: usize = 26;
const AUTH_LEN_MD5: usize = 24;
const AUTH_LEN_SHA1: usize = 28;
const MIN_AUTH_LEN_SIMPLE_PASSWORD: usize = 4;
const MIN_AUTH_LEN_UNKNOWN: usize = 3;
fn diagnostic_name(diag: u8) -> &'static str {
match diag {
0 => "No Diagnostic",
1 => "Control Detection Time Expired",
2 => "Echo Function Failed",
3 => "Neighbor Signaled Session Down",
4 => "Forwarding Plane Reset",
5 => "Path Down",
6 => "Concatenated Path Down",
7 => "Administratively Down",
8 => "Reverse Concatenated Path Down",
_ => "Reserved",
}
}
fn state_name(state: u8) -> &'static str {
match state {
0 => "AdminDown",
1 => "Down",
2 => "Init",
3 => "Up",
_ => unreachable!(),
}
}
fn auth_type_name(auth_type: u8) -> &'static str {
match auth_type {
0 => "Reserved",
1 => "Simple Password",
2 => "Keyed MD5",
3 => "Meticulous Keyed MD5",
4 => "Keyed SHA1",
5 => "Meticulous Keyed SHA1",
_ => "Reserved",
}
}
pub struct BfdDissector;
static FIELD_DESCRIPTORS: &[FieldDescriptor] = &[
FieldDescriptor::new("version", "Version", FieldType::U8),
FieldDescriptor {
name: "diagnostic",
display_name: "Diagnostic",
field_type: FieldType::U8,
optional: false,
children: None,
display_fn: Some(|v, _siblings| match v {
FieldValue::U8(d) => Some(diagnostic_name(*d)),
_ => None,
}),
format_fn: None,
},
FieldDescriptor {
name: "state",
display_name: "State",
field_type: FieldType::U8,
optional: false,
children: None,
display_fn: Some(|v, _siblings| match v {
FieldValue::U8(s) => Some(state_name(*s)),
_ => None,
}),
format_fn: None,
},
FieldDescriptor::new("poll", "Poll", FieldType::U8),
FieldDescriptor::new("final", "Final", FieldType::U8),
FieldDescriptor::new(
"control_plane_independent",
"Control Plane Independent",
FieldType::U8,
),
FieldDescriptor::new("auth_present", "Authentication Present", FieldType::U8),
FieldDescriptor::new("demand", "Demand", FieldType::U8),
FieldDescriptor::new("multipoint", "Multipoint", FieldType::U8),
FieldDescriptor::new("detect_mult", "Detect Multiplier", FieldType::U8),
FieldDescriptor::new("length", "Length", FieldType::U8),
FieldDescriptor::new("my_discriminator", "My Discriminator", FieldType::U32),
FieldDescriptor::new("your_discriminator", "Your Discriminator", FieldType::U32),
FieldDescriptor::new(
"desired_min_tx_interval",
"Desired Min TX Interval",
FieldType::U32,
),
FieldDescriptor::new(
"required_min_rx_interval",
"Required Min RX Interval",
FieldType::U32,
),
FieldDescriptor::new(
"required_min_echo_rx_interval",
"Required Min Echo RX Interval",
FieldType::U32,
),
FieldDescriptor {
name: "auth_type",
display_name: "Auth Type",
field_type: FieldType::U8,
optional: true,
children: None,
display_fn: Some(|v, _siblings| match v {
FieldValue::U8(a) => Some(auth_type_name(*a)),
_ => None,
}),
format_fn: None,
},
FieldDescriptor::new("auth_data", "Auth Data", FieldType::Bytes).optional(),
];
const FD_VERSION: usize = 0;
const FD_DIAGNOSTIC: usize = 1;
const FD_STATE: usize = 2;
const FD_POLL: usize = 3;
const FD_FINAL: usize = 4;
const FD_CONTROL_PLANE_INDEPENDENT: usize = 5;
const FD_AUTH_PRESENT: usize = 6;
const FD_DEMAND: usize = 7;
const FD_MULTIPOINT: usize = 8;
const FD_DETECT_MULT: usize = 9;
const FD_LENGTH: usize = 10;
const FD_MY_DISCRIMINATOR: usize = 11;
const FD_YOUR_DISCRIMINATOR: usize = 12;
const FD_DESIRED_MIN_TX_INTERVAL: usize = 13;
const FD_REQUIRED_MIN_RX_INTERVAL: usize = 14;
const FD_REQUIRED_MIN_ECHO_RX_INTERVAL: usize = 15;
const FD_AUTH_TYPE: usize = 16;
const FD_AUTH_DATA: usize = 17;
impl Dissector for BfdDissector {
fn name(&self) -> &'static str {
"Bidirectional Forwarding Detection"
}
fn short_name(&self) -> &'static str {
"BFD"
}
fn field_descriptors(&self) -> &'static [FieldDescriptor] {
FIELD_DESCRIPTORS
}
fn dissect<'pkt>(
&self,
data: &'pkt [u8],
buf: &mut DissectBuffer<'pkt>,
offset: usize,
) -> Result<DissectResult, PacketError> {
if data.len() < MIN_HEADER_SIZE {
return Err(PacketError::Truncated {
expected: MIN_HEADER_SIZE,
actual: data.len(),
});
}
let byte0 = data[0];
let version = (byte0 >> 5) & 0x07;
let diagnostic = byte0 & 0x1F;
if version != BFD_VERSION {
return Err(PacketError::InvalidFieldValue {
field: "version",
value: u32::from(version),
});
}
let byte1 = data[1];
let state = (byte1 >> 6) & 0x03;
let poll = (byte1 >> 5) & 0x01;
let final_flag = (byte1 >> 4) & 0x01;
let control_plane_independent = (byte1 >> 3) & 0x01;
let auth_present = (byte1 >> 2) & 0x01;
let demand = (byte1 >> 1) & 0x01;
let multipoint = byte1 & 0x01;
let detect_mult = data[2];
if detect_mult == 0 {
return Err(PacketError::InvalidFieldValue {
field: "detect_mult",
value: 0,
});
}
let length_u8 = data[3];
let length = length_u8 as usize;
if length < MIN_HEADER_SIZE {
return Err(PacketError::InvalidFieldValue {
field: "length",
value: length_u8 as u32,
});
}
if data.len() < length {
return Err(PacketError::Truncated {
expected: length,
actual: data.len(),
});
}
let my_discriminator = read_be_u32(data, 4)?;
if my_discriminator == 0 {
return Err(PacketError::InvalidFieldValue {
field: "my_discriminator",
value: 0,
});
}
let your_discriminator = read_be_u32(data, 8)?;
let desired_min_tx = read_be_u32(data, 12)?;
let required_min_rx = read_be_u32(data, 16)?;
let required_min_echo_rx = read_be_u32(data, 20)?;
buf.begin_layer(
self.short_name(),
None,
FIELD_DESCRIPTORS,
offset..offset + length,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_VERSION],
FieldValue::U8(version),
offset..offset + 1,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_DIAGNOSTIC],
FieldValue::U8(diagnostic),
offset..offset + 1,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_STATE],
FieldValue::U8(state),
offset + 1..offset + 2,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_POLL],
FieldValue::U8(poll),
offset + 1..offset + 2,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_FINAL],
FieldValue::U8(final_flag),
offset + 1..offset + 2,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_CONTROL_PLANE_INDEPENDENT],
FieldValue::U8(control_plane_independent),
offset + 1..offset + 2,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_AUTH_PRESENT],
FieldValue::U8(auth_present),
offset + 1..offset + 2,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_DEMAND],
FieldValue::U8(demand),
offset + 1..offset + 2,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_MULTIPOINT],
FieldValue::U8(multipoint),
offset + 1..offset + 2,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_DETECT_MULT],
FieldValue::U8(detect_mult),
offset + 2..offset + 3,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_LENGTH],
FieldValue::U8(length_u8),
offset + 3..offset + 4,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_MY_DISCRIMINATOR],
FieldValue::U32(my_discriminator),
offset + 4..offset + 8,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_YOUR_DISCRIMINATOR],
FieldValue::U32(your_discriminator),
offset + 8..offset + 12,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_DESIRED_MIN_TX_INTERVAL],
FieldValue::U32(desired_min_tx),
offset + 12..offset + 16,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_REQUIRED_MIN_RX_INTERVAL],
FieldValue::U32(required_min_rx),
offset + 16..offset + 20,
);
buf.push_field(
&FIELD_DESCRIPTORS[FD_REQUIRED_MIN_ECHO_RX_INTERVAL],
FieldValue::U32(required_min_echo_rx),
offset + 20..offset + 24,
);
if auth_present == 1 {
if length < MIN_HEADER_SIZE_WITH_AUTH {
return Err(PacketError::InvalidHeader(
"BFD auth present but length is less than minimum with auth",
));
}
let auth_type = data[24];
let auth_len = data[25] as usize;
let min_auth_len = match auth_type {
1 => MIN_AUTH_LEN_SIMPLE_PASSWORD,
2 | 3 => AUTH_LEN_MD5,
4 | 5 => AUTH_LEN_SHA1,
_ => MIN_AUTH_LEN_UNKNOWN,
};
if auth_len < min_auth_len {
return Err(PacketError::InvalidHeader(
"BFD auth length is less than minimum for auth type",
));
}
let auth_end = 24 + auth_len;
if auth_end > length {
return Err(PacketError::InvalidHeader(
"BFD auth section exceeds packet length",
));
}
buf.push_field(
&FIELD_DESCRIPTORS[FD_AUTH_TYPE],
FieldValue::U8(auth_type),
offset + 24..offset + 25,
);
if auth_len > 2 {
buf.push_field(
&FIELD_DESCRIPTORS[FD_AUTH_DATA],
FieldValue::Bytes(&data[26..auth_end]),
offset + 26..offset + auth_end,
);
}
}
buf.end_layer();
Ok(DissectResult::new(length, DispatchHint::End))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(clippy::too_many_arguments)]
fn build_bfd(
version: u8,
diagnostic: u8,
state: u8,
poll: u8,
final_f: u8,
cpi: u8,
auth: u8,
demand: u8,
multipoint: u8,
detect_mult: u8,
length: u8,
my_disc: u32,
your_disc: u32,
desired_min_tx: u32,
required_min_rx: u32,
required_min_echo_rx: u32,
) -> Vec<u8> {
let byte0 = (version << 5) | (diagnostic & 0x1F);
let byte1 = (state << 6)
| (poll << 5)
| (final_f << 4)
| (cpi << 3)
| (auth << 2)
| (demand << 1)
| multipoint;
let mut pkt = Vec::with_capacity(length as usize);
pkt.push(byte0);
pkt.push(byte1);
pkt.push(detect_mult);
pkt.push(length);
pkt.extend_from_slice(&my_disc.to_be_bytes());
pkt.extend_from_slice(&your_disc.to_be_bytes());
pkt.extend_from_slice(&desired_min_tx.to_be_bytes());
pkt.extend_from_slice(&required_min_rx.to_be_bytes());
pkt.extend_from_slice(&required_min_echo_rx.to_be_bytes());
pkt
}
#[allow(clippy::too_many_arguments)]
fn build_bfd_with_auth(
version: u8,
diagnostic: u8,
state: u8,
detect_mult: u8,
my_disc: u32,
your_disc: u32,
auth_type: u8,
auth_data: &[u8],
) -> Vec<u8> {
let auth_len = 2 + auth_data.len();
let total_len = 24 + auth_len;
let mut pkt = build_bfd(
version,
diagnostic,
state,
0,
0,
0,
1, 0,
0,
detect_mult,
total_len as u8,
my_disc,
your_disc,
1_000_000,
1_000_000,
0,
);
pkt.push(auth_type);
pkt.push(auth_len as u8);
pkt.extend_from_slice(auth_data);
pkt
}
#[test]
fn test_parse_basic_up() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 0x0001, 0x0002, 1_000_000, 1_000_000, 0, );
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
assert_eq!(buf.layers().len(), 1);
let layer = &buf.layers()[0];
assert_eq!(layer.name, "BFD");
assert_eq!(
buf.field_by_name(layer, "version").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "diagnostic").unwrap().value,
FieldValue::U8(0)
);
assert_eq!(
buf.resolve_display_name(layer, "diagnostic_name"),
Some("No Diagnostic")
);
assert_eq!(
buf.field_by_name(layer, "state").unwrap().value,
FieldValue::U8(3)
);
assert_eq!(buf.resolve_display_name(layer, "state_name"), Some("Up"));
assert_eq!(
buf.field_by_name(layer, "poll").unwrap().value,
FieldValue::U8(0)
);
assert_eq!(
buf.field_by_name(layer, "final").unwrap().value,
FieldValue::U8(0)
);
assert_eq!(
buf.field_by_name(layer, "control_plane_independent")
.unwrap()
.value,
FieldValue::U8(0)
);
assert_eq!(
buf.field_by_name(layer, "auth_present").unwrap().value,
FieldValue::U8(0)
);
assert_eq!(
buf.field_by_name(layer, "demand").unwrap().value,
FieldValue::U8(0)
);
assert_eq!(
buf.field_by_name(layer, "multipoint").unwrap().value,
FieldValue::U8(0)
);
assert_eq!(
buf.field_by_name(layer, "detect_mult").unwrap().value,
FieldValue::U8(3)
);
assert_eq!(
buf.field_by_name(layer, "length").unwrap().value,
FieldValue::U8(24)
);
assert_eq!(
buf.field_by_name(layer, "my_discriminator").unwrap().value,
FieldValue::U32(0x0001)
);
assert_eq!(
buf.field_by_name(layer, "your_discriminator")
.unwrap()
.value,
FieldValue::U32(0x0002)
);
assert_eq!(
buf.field_by_name(layer, "desired_min_tx_interval")
.unwrap()
.value,
FieldValue::U32(1_000_000)
);
assert_eq!(
buf.field_by_name(layer, "required_min_rx_interval")
.unwrap()
.value,
FieldValue::U32(1_000_000)
);
assert_eq!(
buf.field_by_name(layer, "required_min_echo_rx_interval")
.unwrap()
.value,
FieldValue::U32(0)
);
}
#[test]
fn test_parse_all_flags_set() {
let mut data = build_bfd(
1, 7, 0, 1, 1, 1, 1, 1, 1, 5, 28, 0xAABBCCDD, 0x11223344, 500_000, 500_000, 100_000,
);
data.push(1); data.push(4); data.push(0x41); data.push(0x42);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
let layer = &buf.layers()[0];
assert_eq!(
buf.field_by_name(layer, "poll").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "final").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "control_plane_independent")
.unwrap()
.value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "auth_present").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "demand").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "multipoint").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "auth_type").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.resolve_display_name(layer, "auth_type_name"),
Some("Simple Password")
);
assert_eq!(
buf.field_by_name(layer, "auth_data").unwrap().value,
FieldValue::Bytes(&[0x41, 0x42])
);
}
#[test]
fn test_diagnostic_codes() {
for diag in 0..=8 {
let data = build_bfd(
1, diag, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
let layer = &buf.layers()[0];
assert_eq!(
buf.field_by_name(layer, "diagnostic").unwrap().value,
FieldValue::U8(diag)
);
if let Some(name) = buf.resolve_display_name(layer, "diagnostic_name") {
assert!(!name.is_empty());
assert_ne!(name, "Reserved");
} else {
panic!("diagnostic_name should be Str");
}
}
let data = build_bfd(
1, 9, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
assert_eq!(
buf.resolve_display_name(&buf.layers()[0], "diagnostic_name"),
Some("Reserved")
);
}
#[test]
fn test_state_values() {
let names = ["AdminDown", "Down", "Init", "Up"];
for state in 0..=3u8 {
let data = build_bfd(
1, 0, state, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
let layer = &buf.layers()[0];
assert_eq!(
buf.field_by_name(layer, "state").unwrap().value,
FieldValue::U8(state)
);
assert_eq!(
buf.resolve_display_name(layer, "state_name"),
Some(names[state as usize])
);
}
}
#[test]
fn test_parse_with_auth_simple() {
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);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
let layer = &buf.layers()[0];
assert_eq!(
buf.field_by_name(layer, "auth_present").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.field_by_name(layer, "auth_type").unwrap().value,
FieldValue::U8(1)
);
assert_eq!(
buf.resolve_display_name(layer, "auth_type_name"),
Some("Simple Password")
);
assert_eq!(
buf.field_by_name(layer, "auth_data").unwrap().value,
FieldValue::Bytes(&auth_data)
);
}
#[test]
fn test_parse_with_auth_sha1() {
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);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
let layer = &buf.layers()[0];
assert_eq!(
buf.field_by_name(layer, "auth_type").unwrap().value,
FieldValue::U8(4)
);
assert_eq!(
buf.resolve_display_name(layer, "auth_type_name"),
Some("Keyed SHA1")
);
}
#[test]
fn test_truncated_packet() {
let data = [0u8; 23]; let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
assert!(result.is_err());
match result.unwrap_err() {
PacketError::Truncated { expected, actual } => {
assert_eq!(expected, 24);
assert_eq!(actual, 23);
}
other => panic!("Expected Truncated, got {other:?}"),
}
}
#[test]
fn test_invalid_length_field() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 20, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
assert!(result.is_err());
match result.unwrap_err() {
PacketError::InvalidFieldValue { field, value } => {
assert_eq!(field, "length");
assert_eq!(value, 20);
}
other => panic!("Expected InvalidFieldValue, got {other:?}"),
}
}
#[test]
fn test_auth_present_but_truncated() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
assert!(result.is_err());
match result.unwrap_err() {
PacketError::InvalidHeader(msg) => {
assert!(msg.contains("auth present"));
}
other => panic!("Expected InvalidHeader, got {other:?}"),
}
}
#[test]
fn test_dissect_with_offset() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let offset = 42; let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, offset).unwrap();
let layer = &buf.layers()[0];
assert_eq!(layer.range, offset..offset + 24);
assert_eq!(
buf.field_by_name(layer, "required_min_echo_rx_interval")
.unwrap()
.range,
offset + 20..offset + 24
);
}
#[test]
fn test_field_descriptors() {
let descriptors = BfdDissector.field_descriptors();
assert_eq!(descriptors.len(), 18);
assert_eq!(descriptors[0].name, "version");
assert_eq!(descriptors[descriptors.len() - 1].name, "auth_data");
assert!(!descriptors[0].optional); assert!(descriptors[16].optional); assert!(descriptors[17].optional); }
#[test]
fn test_length_exceeds_data() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 30, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
assert!(result.is_err());
match result.unwrap_err() {
PacketError::Truncated { expected, actual } => {
assert_eq!(expected, 30);
assert_eq!(actual, 24);
}
other => panic!("Expected Truncated, got {other:?}"),
}
}
#[test]
fn test_invalid_version() {
let data = build_bfd(
0, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidFieldValue { field, value } => {
assert_eq!(field, "version");
assert_eq!(value, 0);
}
other => panic!("Expected InvalidFieldValue, got {other:?}"),
}
let data = build_bfd(
2, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidFieldValue { field, value } => {
assert_eq!(field, "version");
assert_eq!(value, 2);
}
other => panic!("Expected InvalidFieldValue, got {other:?}"),
}
}
#[test]
fn test_detect_mult_zero() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidFieldValue { field, value } => {
assert_eq!(field, "detect_mult");
assert_eq!(value, 0);
}
other => panic!("Expected InvalidFieldValue, got {other:?}"),
}
}
#[test]
fn test_my_discriminator_zero() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 0, 0, 0, 3, 24, 0, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&data, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidFieldValue { field, value } => {
assert_eq!(field, "my_discriminator");
assert_eq!(value, 0);
}
other => panic!("Expected InvalidFieldValue, got {other:?}"),
}
}
#[test]
fn test_parse_multipoint_bit_set() {
let data = build_bfd(
1, 0, 3, 0, 0, 0, 0, 0, 1, 3, 24, 1, 0, 1_000_000, 1_000_000, 0,
);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
let layer = &buf.layers()[0];
assert_eq!(
buf.field_by_name(layer, "multipoint").unwrap().value,
FieldValue::U8(1)
);
}
#[test]
fn test_simple_password_too_short() {
let mut pkt = build_bfd(
1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 27, 1, 0, 1_000_000, 1_000_000, 0,
);
pkt.push(1); pkt.push(3); pkt.push(1); let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&pkt, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidHeader(msg) => {
assert!(msg.contains("auth length"), "unexpected message: {msg}");
}
other => panic!("Expected InvalidHeader, got {other:?}"),
}
}
#[test]
fn test_parse_with_auth_md5() {
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);
let mut buf = DissectBuffer::new();
BfdDissector.dissect(&data, &mut buf, 0).unwrap();
let layer = &buf.layers()[0];
assert_eq!(
buf.field_by_name(layer, "auth_type").unwrap().value,
FieldValue::U8(2)
);
assert_eq!(
buf.resolve_display_name(layer, "auth_type_name"),
Some("Keyed MD5")
);
}
#[test]
fn test_md5_wrong_length() {
let mut pkt = build_bfd(
1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 44, 1, 0, 1_000_000, 1_000_000, 0,
);
pkt.push(2); pkt.push(20); pkt.extend_from_slice(&[0u8; 18]); let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&pkt, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidHeader(msg) => {
assert!(msg.contains("auth length"), "unexpected message: {msg}");
}
other => panic!("Expected InvalidHeader, got {other:?}"),
}
}
#[test]
fn test_sha1_wrong_length() {
let mut pkt = build_bfd(
1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 48, 1, 0, 1_000_000, 1_000_000, 0,
);
pkt.push(4); pkt.push(24); pkt.extend_from_slice(&[0u8; 22]); let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&pkt, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidHeader(msg) => {
assert!(msg.contains("auth length"), "unexpected message: {msg}");
}
other => panic!("Expected InvalidHeader, got {other:?}"),
}
}
#[test]
fn test_auth_section_exceeds_length() {
let mut pkt = build_bfd(
1, 0, 3, 0, 0, 0, 1, 0, 0, 3, 28, 1, 0, 1_000_000, 1_000_000, 0,
);
pkt.push(1); pkt.push(10); pkt.push(1);
pkt.push(b'x');
let mut buf = DissectBuffer::new();
let result = BfdDissector.dissect(&pkt, &mut buf, 0);
match result.unwrap_err() {
PacketError::InvalidHeader(msg) => {
assert!(msg.contains("exceeds"), "unexpected message: {msg}");
}
other => panic!("Expected InvalidHeader, got {other:?}"),
}
}
}