use super::{Arp, ArpOperation, Ethernet, ETHERTYPE_ARP};
use crate::{CrafterError, LinkType, MacAddr, Packet, Raw};
use core::net::Ipv4Addr;
const ARP_REQUEST_FIXTURE: &[u8] = fixture_bytes!("bytes/arp-who-has.bin");
fn src_mac() -> MacAddr {
"02:00:5e:00:53:01".parse().unwrap()
}
const ARP_REPLY_GOLDEN: &[u8] = &[
0x02, 0x00, 0x5e, 0x00, 0x53, 0x01, 0x02, 0x00, 0x5e, 0x00, 0x53, 0x02, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x02, 0x02, 0x00, 0x5e, 0x00, 0x53, 0x02, 0xc0, 0x00, 0x02, 0x01, 0x02, 0x00, 0x5e, 0x00, 0x53, 0x01, 0xc0, 0x00, 0x02, 0x0a, ];
fn sender_mac() -> MacAddr {
"02:00:5e:00:53:02".parse().unwrap()
}
#[test]
fn arp_request_matches_golden_bytes() {
let packet = Ethernet::new().src(src_mac())
/ Arp::who_has(
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 1),
src_mac(),
);
assert_eq!(packet.compile().unwrap().as_bytes(), ARP_REQUEST_FIXTURE);
}
#[test]
fn arp_reply_builder_matches_golden_bytes() {
let packet = Ethernet::new().src(sender_mac()).dst(src_mac())
/ Arp::is_at(
Ipv4Addr::new(192, 0, 2, 1),
sender_mac(),
Ipv4Addr::new(192, 0, 2, 10),
src_mac(),
);
assert_eq!(packet.compile().unwrap().as_bytes(), ARP_REPLY_GOLDEN);
}
#[test]
fn arp_who_has_builder_sets_request_fields_and_autofills_ethertype() {
let arp = Arp::who_has(
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 1),
src_mac(),
);
assert_eq!(arp.opcode_value(), ArpOperation::Request as u16);
assert_eq!(arp.sender_mac(), Some(src_mac()));
assert_eq!(arp.sender_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 10)));
assert_eq!(arp.target_mac(), Some(MacAddr::ZERO));
assert_eq!(arp.target_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 1)));
let frame = (Ethernet::new().src(src_mac()) / arp).compile().unwrap();
assert_eq!(&frame.as_bytes()[12..14], ÐERTYPE_ARP.to_be_bytes());
}
#[test]
fn arp_is_at_builder_sets_reply_fields() {
let arp = Arp::is_at(
Ipv4Addr::new(192, 0, 2, 1),
sender_mac(),
Ipv4Addr::new(192, 0, 2, 10),
src_mac(),
);
assert_eq!(arp.opcode_value(), ArpOperation::Reply as u16);
assert_eq!(arp.sender_mac(), Some(sender_mac()));
assert_eq!(arp.sender_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 1)));
assert_eq!(arp.target_mac(), Some(src_mac()));
assert_eq!(arp.target_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 10)));
}
#[test]
fn arp_compile_decode_round_trip_preserves_standard_request_fields() {
let packet = Ethernet::new().src(src_mac())
/ Arp::who_has(
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 1),
src_mac(),
);
let compiled = packet.compile().unwrap();
let wire = compiled.as_bytes().to_vec();
let decoded = Packet::decode_from_link(LinkType::Ethernet, &wire).unwrap();
let arp = decoded.layer::<Arp>().unwrap();
assert_eq!(arp.hardware_type_value(), 1);
assert_eq!(arp.protocol_type_value(), super::ETHERTYPE_IPV4);
assert_eq!(arp.hardware_len_value(), 6);
assert_eq!(arp.protocol_len_value(), 4);
assert_eq!(arp.opcode_value(), ArpOperation::Request as u16);
assert_eq!(arp.sender_mac(), Some(src_mac()));
assert_eq!(arp.sender_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 10)));
assert_eq!(arp.target_mac(), Some(MacAddr::ZERO));
assert_eq!(arp.target_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 1)));
assert_eq!(decoded.compile().unwrap().as_bytes(), wire.as_slice());
}
#[test]
fn arp_decode_exposes_ipv4_and_mac_fields() {
let decoded = Packet::decode_from_link(LinkType::Ethernet, ARP_REQUEST_FIXTURE).unwrap();
let arp = decoded.layer::<Arp>().unwrap();
assert_eq!(arp.hardware_type_value(), 1);
assert_eq!(arp.protocol_type_value(), super::ETHERTYPE_IPV4);
assert_eq!(arp.opcode_value(), ArpOperation::Request as u16);
assert_eq!(arp.sender_mac(), Some(src_mac()));
assert_eq!(arp.target_mac(), Some(MacAddr::ZERO));
assert_eq!(arp.sender_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 10)));
assert_eq!(arp.target_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 1)));
assert_eq!(decoded.compile().unwrap().as_bytes(), ARP_REQUEST_FIXTURE);
}
#[test]
fn arp_rejects_inconsistent_address_lengths() {
let packet = Packet::new().push(
Arp::new()
.hardware_len(5)
.sender_hardware_addr(src_mac())
.target_hardware_addr(MacAddr::ZERO),
);
let err = packet.compile().unwrap_err();
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "arp.sender_hardware_addr");
assert_eq!(required, 5);
assert_eq!(available, 6);
}
other => panic!("expected a structured length error, got {other:?}"),
}
}
#[test]
fn arp_length_matching_explicit_lengths_compile_without_false_positive() {
let sender_hw = vec![0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11];
let sender_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
];
let target_hw = vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
let target_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02,
];
let arp = Arp::new()
.hardware_len(8)
.protocol_len(16)
.sender_hardware_bytes(sender_hw.clone())
.sender_protocol_bytes(sender_pa.clone())
.target_hardware_bytes(target_hw.clone())
.target_protocol_bytes(target_pa.clone());
let frame = Ethernet::new().src(src_mac()) / arp;
let compiled = frame.compile().expect("matching lengths must compile");
let decoded = Packet::decode_from_link(LinkType::Ethernet, compiled.as_bytes()).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_len_value(), 8);
assert_eq!(decoded_arp.protocol_len_value(), 16);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(decoded_arp.target_protocol_bytes_value(), target_pa);
}
#[test]
fn arp_length_mismatch_on_protocol_field_returns_structured_error() {
let packet = Packet::new().push(
Arp::new()
.protocol_len(6)
.sender_protocol_bytes(vec![192, 0, 2, 10]),
);
let err = packet.compile().unwrap_err();
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "arp.sender_protocol_addr");
assert_eq!(required, 6);
assert_eq!(available, 4);
}
other => panic!("expected a structured length error, got {other:?}"),
}
}
#[test]
fn arp_length_mismatch_on_target_field_returns_structured_error() {
let packet = Packet::new().push(
Arp::new()
.hardware_len(6)
.target_hardware_bytes(vec![0xde, 0xad, 0xbe]),
);
let err = packet.compile().unwrap_err();
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "arp.target_hardware_addr");
assert_eq!(required, 6);
assert_eq!(available, 3);
}
other => panic!("expected a structured length error, got {other:?}"),
}
}
#[test]
fn arp_length_zero_length_addresses_compile_and_round_trip() {
let arp = Arp::new()
.hardware_len(0)
.protocol_len(0)
.sender_hardware_bytes(Vec::new())
.sender_protocol_bytes(Vec::new())
.target_hardware_bytes(Vec::new())
.target_protocol_bytes(Vec::new());
let frame = Ethernet::new().src(src_mac()) / arp;
let compiled = frame.compile().expect("zero-length ARP must compile");
let decoded = Packet::decode_from_link(LinkType::Ethernet, compiled.as_bytes()).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_len_value(), 0);
assert_eq!(decoded_arp.protocol_len_value(), 0);
assert!(decoded_arp.sender_hardware_bytes_value().is_empty());
assert!(decoded_arp.target_protocol_bytes_value().is_empty());
}
#[test]
fn arp_length_zero_length_field_against_nonempty_bytes_is_a_conflict() {
let packet = Packet::new().push(Arp::new().protocol_len(0).sender_protocol_bytes(vec![0x01]));
let err = packet.compile().unwrap_err();
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "arp.sender_protocol_addr");
assert_eq!(required, 0);
assert_eq!(available, 1);
}
other => panic!("expected a structured length error, got {other:?}"),
}
}
#[test]
fn arp_length_maximum_u8_length_arithmetic_does_not_overflow() {
let max = u8::MAX;
let full = vec![0x5a_u8; max as usize];
let arp = Arp::new()
.hardware_len(max)
.protocol_len(max)
.sender_hardware_bytes(full.clone())
.sender_protocol_bytes(full.clone())
.target_hardware_bytes(full.clone())
.target_protocol_bytes(full.clone());
assert_eq!(arp.hardware_len_value(), max);
assert_eq!(arp.protocol_len_value(), max);
let frame = Ethernet::new().src(src_mac()) / arp;
let compiled = frame
.compile()
.expect("maximum-width ARP addresses must compile");
let decoded = Packet::decode_from_link(LinkType::Ethernet, compiled.as_bytes()).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_len_value(), max);
assert_eq!(decoded_arp.protocol_len_value(), max);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), full);
assert_eq!(decoded_arp.target_protocol_bytes_value(), full);
}
#[test]
fn arp_length_oversized_byte_vector_saturates_then_conflicts() {
let oversized = vec![0u8; 300];
let arp = Arp::new().sender_hardware(oversized);
assert_eq!(arp.hardware_len_value(), u8::MAX);
let err = (Ethernet::new().src(src_mac()) / arp)
.compile()
.unwrap_err();
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "arp.sender_hardware_addr");
assert_eq!(required, u8::MAX as usize);
assert_eq!(available, 300);
}
other => panic!("expected a structured length error, got {other:?}"),
}
}
#[test]
fn arp_reply_builder_sets_expected_operation() {
let arp = Arp::is_at(
Ipv4Addr::new(192, 0, 2, 10),
src_mac(),
Ipv4Addr::new(192, 0, 2, 1),
MacAddr::BROADCAST,
);
assert_eq!(arp.opcode_value(), ArpOperation::Reply as u16);
assert_eq!(arp.sender_mac(), Some(src_mac()));
}
#[test]
fn arp_constants_match_source_backed_codepoints() {
use super::{
ARP_HRD_ATM, ARP_HRD_ETHERNET, ARP_HRD_FIBRE_CHANNEL, ARP_HRD_IEEE_802, ARP_HRD_INFINIBAND,
ARP_HRD_MAPOS, ARP_OP_ARP_NAK, ARP_OP_DRARP_ERROR, ARP_OP_DRARP_REPLY,
ARP_OP_DRARP_REQUEST, ARP_OP_EXP1, ARP_OP_EXP2, ARP_OP_INARP_REPLY, ARP_OP_INARP_REQUEST,
ARP_OP_MAPOS_UNARP, ARP_OP_RARP_REPLY, ARP_OP_RARP_REQUEST, ARP_OP_REPLY, ARP_OP_REQUEST,
ARP_OP_RESERVED, ARP_OP_RESERVED_MAX, ARP_PRO_IPV4, ETHERTYPE_IPV4,
};
assert_eq!(ARP_OP_RESERVED, 0);
assert_eq!(ARP_OP_REQUEST, 1);
assert_eq!(ARP_OP_REPLY, 2);
assert_eq!(ARP_OP_RARP_REQUEST, 3);
assert_eq!(ARP_OP_RARP_REPLY, 4);
assert_eq!(ARP_OP_DRARP_REQUEST, 5);
assert_eq!(ARP_OP_DRARP_REPLY, 6);
assert_eq!(ARP_OP_DRARP_ERROR, 7);
assert_eq!(ARP_OP_INARP_REQUEST, 8);
assert_eq!(ARP_OP_INARP_REPLY, 9);
assert_eq!(ARP_OP_ARP_NAK, 10);
assert_eq!(ARP_OP_MAPOS_UNARP, 23);
assert_eq!(ARP_OP_EXP1, 24);
assert_eq!(ARP_OP_EXP2, 25);
assert_eq!(ARP_OP_RESERVED_MAX, 65535);
assert_eq!(ARP_OP_REQUEST, ArpOperation::Request as u16);
assert_eq!(ARP_OP_REPLY, ArpOperation::Reply as u16);
assert_eq!(ARP_HRD_ETHERNET, 1);
assert_eq!(ARP_HRD_IEEE_802, 6);
assert_eq!(ARP_HRD_FIBRE_CHANNEL, 18);
assert_eq!(ARP_HRD_ATM, 19);
assert_eq!(ARP_HRD_MAPOS, 25);
assert_eq!(ARP_HRD_INFINIBAND, 32);
assert_eq!(ARP_PRO_IPV4, ETHERTYPE_IPV4);
}
#[test]
fn arp_constants_drive_builder_and_preserve_unknown_values() {
use super::{ARP_HRD_INFINIBAND, ARP_OP_INARP_REQUEST};
let arp = Arp::new()
.hardware_type(ARP_HRD_INFINIBAND)
.opcode(ARP_OP_INARP_REQUEST);
assert_eq!(arp.hardware_type_value(), ARP_HRD_INFINIBAND);
assert_eq!(arp.opcode_value(), ARP_OP_INARP_REQUEST);
let unknown_op: u16 = 0x0fa0;
let unknown = Arp::new().opcode(unknown_op);
assert_eq!(unknown.opcode_value(), unknown_op);
}
#[test]
fn arp_constants_reexported_through_prelude() {
use crate::prelude::{ARP_OP_REPLY, ARP_OP_REQUEST};
assert_eq!(ARP_OP_REQUEST, 1);
assert_eq!(ARP_OP_REPLY, 2);
}
#[test]
fn arp_operation_names_known_source_backed_codepoints() {
use super::{
ARP_OP_ARP_NAK, ARP_OP_DRARP_ERROR, ARP_OP_DRARP_REPLY, ARP_OP_DRARP_REQUEST,
ARP_OP_INARP_REPLY, ARP_OP_INARP_REQUEST, ARP_OP_MAPOS_UNARP, ARP_OP_RARP_REPLY,
ARP_OP_RARP_REQUEST, ARP_OP_REPLY, ARP_OP_REQUEST,
};
use crate::packet::Layer;
let named = [
(ArpOperation::Request, ARP_OP_REQUEST),
(ArpOperation::Reply, ARP_OP_REPLY),
(ArpOperation::RarpRequest, ARP_OP_RARP_REQUEST),
(ArpOperation::RarpReply, ARP_OP_RARP_REPLY),
(ArpOperation::DrarpRequest, ARP_OP_DRARP_REQUEST),
(ArpOperation::DrarpReply, ARP_OP_DRARP_REPLY),
(ArpOperation::DrarpError, ARP_OP_DRARP_ERROR),
(ArpOperation::InArpRequest, ARP_OP_INARP_REQUEST),
(ArpOperation::InArpReply, ARP_OP_INARP_REPLY),
(ArpOperation::ArpNak, ARP_OP_ARP_NAK),
(ArpOperation::MaposUnarp, ARP_OP_MAPOS_UNARP),
];
for (operation, opcode) in named {
assert_eq!(operation.opcode(), opcode);
assert_eq!(u16::from(operation), opcode);
assert_eq!(ArpOperation::from_opcode(opcode), Some(operation));
assert_eq!(ArpOperation::try_from(opcode), Ok(operation));
let arp = Arp::new().operation(operation);
assert_eq!(arp.opcode_value(), opcode);
}
assert_eq!(ArpOperation::Request.label(), "request");
assert_eq!(ArpOperation::Reply.label(), "reply");
assert_eq!(ArpOperation::InArpRequest.label(), "inarp-request");
let reply = Arp::new().operation(ArpOperation::Reply);
assert!(reply.summary().contains("op=reply"));
let inarp = Arp::new().operation(ArpOperation::InArpRequest);
assert!(inarp.summary().contains("op=inarp-request"));
}
#[test]
fn arp_unknown_opcode_stays_numeric_and_round_trips() {
use crate::packet::Layer;
use crate::{LinkType, Packet};
for unknown in [0_u16, 11, 22, 24, 25, 1024, 0x1234, 65279, 65535] {
assert_eq!(ArpOperation::from_opcode(unknown), None);
assert_eq!(ArpOperation::try_from(unknown), Err(unknown));
let arp = Arp::new().opcode(unknown);
assert_eq!(arp.opcode_value(), unknown);
assert!(arp.summary().contains(&format!("op={unknown}")));
}
let unknown_op: u16 = 0x0fa0;
let packet = Ethernet::new().src(src_mac())
/ Arp::who_has(
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 1),
src_mac(),
)
.opcode(unknown_op);
let bytes = packet.compile().unwrap();
let decoded = Packet::decode_from_link(LinkType::Ethernet, bytes.as_bytes()).unwrap();
let arp = decoded.layer::<Arp>().unwrap();
assert_eq!(arp.opcode_value(), unknown_op);
assert_eq!(ArpOperation::from_opcode(arp.opcode_value()), None);
}
#[test]
fn arp_hardware_type_labels_known_source_backed_codepoints() {
use super::{
arp_hardware_type_label, ARP_HRD_ATM, ARP_HRD_ETHERNET, ARP_HRD_FIBRE_CHANNEL,
ARP_HRD_IEEE_802, ARP_HRD_INFINIBAND, ARP_HRD_MAPOS,
};
let named = [
(ARP_HRD_ETHERNET, "ethernet"),
(ARP_HRD_IEEE_802, "ieee-802"),
(ARP_HRD_FIBRE_CHANNEL, "fibre-channel"),
(ARP_HRD_ATM, "atm"),
(ARP_HRD_MAPOS, "mapos"),
(ARP_HRD_INFINIBAND, "infiniband"),
];
for (value, label) in named {
assert_eq!(arp_hardware_type_label(value), Some(label));
let arp = Arp::new().hardware_type(value);
assert_eq!(arp.hardware_type_value(), value);
assert_eq!(arp.hardware_type_label(), Some(label));
}
}
#[test]
fn arp_hardware_type_unknown_values_stay_raw_and_unlabeled() {
use super::{arp_hardware_type_label, ARP_HRD_ETHERNET};
for unknown in [0_u16, 2, 7, 100, 0x1234, 65535] {
assert_eq!(arp_hardware_type_label(unknown), None);
let arp = Arp::new().hardware_type(unknown);
assert_eq!(arp.hardware_type_value(), unknown);
assert_eq!(arp.hardware_type_label(), None);
}
let default_arp = Arp::new();
assert_eq!(default_arp.hardware_type_value(), ARP_HRD_ETHERNET);
assert_eq!(default_arp.hardware_type_label(), Some("ethernet"));
}
#[test]
fn arp_hardware_type_label_reexported_through_prelude() {
use crate::prelude::{arp_hardware_type_label, ARP_HRD_INFINIBAND};
assert_eq!(
arp_hardware_type_label(ARP_HRD_INFINIBAND),
Some("infiniband")
);
assert_eq!(arp_hardware_type_label(0x4242), None);
}
#[test]
fn arp_protocol_type_label_known_source_backed_codepoint() {
use super::{arp_protocol_type_label, ARP_PRO_IPV4, ETHERTYPE_IPV4};
assert_eq!(ARP_PRO_IPV4, ETHERTYPE_IPV4);
assert_eq!(arp_protocol_type_label(ARP_PRO_IPV4), Some("ipv4"));
let arp = Arp::new().protocol_type(ARP_PRO_IPV4);
assert_eq!(arp.protocol_type_value(), ARP_PRO_IPV4);
assert_eq!(arp.protocol_type_label(), Some("ipv4"));
let default_arp = Arp::new();
assert_eq!(default_arp.protocol_type_value(), ETHERTYPE_IPV4);
assert_eq!(default_arp.protocol_type_label(), Some("ipv4"));
}
#[test]
fn arp_protocol_type_unknown_values_stay_raw_and_unlabeled() {
use super::arp_protocol_type_label;
for unknown in [0_u16, 0x0805, 0x86dd, 0x1234, 65535] {
assert_eq!(arp_protocol_type_label(unknown), None);
let arp = Arp::new().protocol_type(unknown);
assert_eq!(arp.protocol_type_value(), unknown);
assert_eq!(arp.protocol_type_label(), None);
}
}
#[test]
fn arp_protocol_type_label_reexported_through_prelude() {
use crate::prelude::{arp_protocol_type_label, ARP_PRO_IPV4};
assert_eq!(arp_protocol_type_label(ARP_PRO_IPV4), Some("ipv4"));
assert_eq!(arp_protocol_type_label(0x4242), None);
}
#[test]
fn arp_raw_address_builders_fill_lengths_from_byte_count() {
let sender_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x10, 0xaa, 0xbb];
let sender_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
];
let target_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x20, 0xcc, 0xdd];
let target_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02,
];
let arp = Arp::new()
.sender_hardware(sender_hw.clone())
.sender_protocol(sender_pa.clone())
.target_hardware(target_hw.clone())
.target_protocol(target_pa.clone());
assert_eq!(arp.hardware_len_value(), 8);
assert_eq!(arp.protocol_len_value(), 16);
assert_eq!(arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(arp.target_hardware_bytes_value(), target_hw);
assert_eq!(arp.target_protocol_bytes_value(), target_pa);
let frame = Ethernet::new().src(src_mac()) / arp;
let bytes = frame.compile().unwrap().as_bytes().to_vec();
let decoded = Packet::decode_from_link(LinkType::Ethernet, &bytes).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_len_value(), 8);
assert_eq!(decoded_arp.protocol_len_value(), 16);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(decoded_arp.target_protocol_bytes_value(), target_pa);
}
#[test]
fn arp_raw_address_builders_honor_explicit_length_override() {
let arp = Arp::new()
.hardware_len(5)
.sender_hardware(vec![0xde, 0xad, 0xbe, 0xef, 0x00, 0x11]);
assert_eq!(arp.hardware_len_value(), 5);
assert_eq!(
arp.sender_hardware_bytes_value(),
vec![0xde, 0xad, 0xbe, 0xef, 0x00, 0x11]
);
let arp = Arp::new()
.target_protocol(vec![0x0a, 0x0b, 0x0c, 0x0d])
.protocol_len(2);
assert_eq!(arp.protocol_len_value(), 2);
assert_eq!(
arp.target_protocol_bytes_value(),
vec![0x0a, 0x0b, 0x0c, 0x0d]
);
}
#[test]
fn arp_raw_address_builders_accept_zero_length_addresses() {
let arp = Arp::new()
.sender_hardware(Vec::<u8>::new())
.sender_protocol(Vec::<u8>::new())
.target_hardware(Vec::<u8>::new())
.target_protocol(Vec::<u8>::new());
assert_eq!(arp.hardware_len_value(), 0);
assert_eq!(arp.protocol_len_value(), 0);
assert!(arp.sender_hardware_bytes_value().is_empty());
let frame = Ethernet::new().src(src_mac()) / arp;
let bytes = frame.compile().unwrap().as_bytes().to_vec();
let decoded = Packet::decode_from_link(LinkType::Ethernet, &bytes).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_len_value(), 0);
assert_eq!(decoded_arp.protocol_len_value(), 0);
}
#[test]
fn arp_raw_address_builder_saturates_oversized_length() {
let oversized = vec![0u8; 300];
let arp = Arp::new().sender_hardware(oversized);
assert_eq!(arp.hardware_len_value(), u8::MAX);
assert_eq!(arp.sender_hardware_bytes_value().len(), 300);
}
#[test]
fn arp_preserves_explicit_fields_through_compile_and_decode() {
let sender_hw = vec![0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11];
let sender_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
];
let target_hw = vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
let target_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02,
];
let arp = Arp::new()
.hardware_type(super::ARP_HRD_INFINIBAND)
.protocol_type(0x86dd)
.hardware_len(8)
.protocol_len(16)
.opcode(0x0fa0)
.sender_hardware_bytes(sender_hw.clone())
.sender_protocol_bytes(sender_pa.clone())
.target_hardware_bytes(target_hw.clone())
.target_protocol_bytes(target_pa.clone());
assert_eq!(arp.hardware_type_value(), super::ARP_HRD_INFINIBAND);
assert_eq!(arp.protocol_type_value(), 0x86dd);
assert_eq!(arp.hardware_len_value(), 8);
assert_eq!(arp.protocol_len_value(), 16);
assert_eq!(arp.opcode_value(), 0x0fa0);
assert_eq!(arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(arp.target_hardware_bytes_value(), target_hw);
assert_eq!(arp.target_protocol_bytes_value(), target_pa);
let frame = Ethernet::new().src(src_mac()) / arp;
let bytes = frame.compile().unwrap().as_bytes().to_vec();
let decoded = Packet::decode_from_link(LinkType::Ethernet, &bytes).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_type_value(), super::ARP_HRD_INFINIBAND);
assert_eq!(decoded_arp.protocol_type_value(), 0x86dd);
assert_eq!(decoded_arp.hardware_len_value(), 8);
assert_eq!(decoded_arp.protocol_len_value(), 16);
assert_eq!(decoded_arp.opcode_value(), 0x0fa0);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(decoded_arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(decoded_arp.target_hardware_bytes_value(), target_hw);
assert_eq!(decoded_arp.target_protocol_bytes_value(), target_pa);
}
#[test]
fn arp_preserves_intentionally_malformed_address_bytes() {
let arp = Arp::new()
.hardware_len(3)
.protocol_len(1)
.opcode(0)
.sender_hardware_bytes(vec![0xde, 0xad, 0xbe])
.sender_protocol_bytes(vec![0x01])
.target_hardware_bytes(vec![0xca, 0xfe, 0x99])
.target_protocol_bytes(vec![0x02]);
let frame = Ethernet::new().src(src_mac()) / arp;
let bytes = frame.compile().unwrap().as_bytes().to_vec();
let decoded = Packet::decode_from_link(LinkType::Ethernet, &bytes).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_len_value(), 3);
assert_eq!(decoded_arp.protocol_len_value(), 1);
assert_eq!(decoded_arp.opcode_value(), 0);
assert_eq!(
decoded_arp.sender_hardware_bytes_value(),
vec![0xde, 0xad, 0xbe]
);
assert_eq!(decoded_arp.sender_protocol_bytes_value(), vec![0x01]);
assert_eq!(
decoded_arp.target_hardware_bytes_value(),
vec![0xca, 0xfe, 0x99]
);
assert_eq!(decoded_arp.target_protocol_bytes_value(), vec![0x02]);
}
#[test]
fn arp_preserves_user_values_against_later_helper_defaults() {
let arp = Arp::new()
.hardware_len(9)
.protocol_len(7)
.protocol_type(0xbeef)
.sender_hardware_addr(MacAddr::ZERO)
.sender_protocol_addr(Ipv4Addr::new(192, 0, 2, 10))
.target_hardware_addr(MacAddr::BROADCAST)
.target_protocol_addr(Ipv4Addr::new(192, 0, 2, 1));
assert_eq!(arp.hardware_len_value(), 9);
assert_eq!(arp.protocol_len_value(), 7);
assert_eq!(arp.protocol_type_value(), 0xbeef);
assert_eq!(
arp.sender_hardware_bytes_value(),
MacAddr::ZERO.octets().to_vec()
);
assert_eq!(
arp.target_protocol_bytes_value(),
Ipv4Addr::new(192, 0, 2, 1).octets().to_vec()
);
}
#[test]
fn arp_override_of_helper_defaulted_lengths_and_types_is_honored() {
let arp = Arp::new()
.sender_hardware(vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x10, 0xaa, 0xbb])
.hardware_len(4)
.protocol_type(0x86dd);
assert_eq!(arp.hardware_len_value(), 4);
assert_eq!(arp.protocol_type_value(), 0x86dd);
assert_eq!(
arp.sender_hardware_bytes_value(),
vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x10, 0xaa, 0xbb]
);
}
#[test]
fn arp_override_of_opcode_and_types_survives_who_has_helper() {
let arp = Arp::who_has(
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 1),
src_mac(),
)
.opcode(0x1234)
.hardware_type(super::ARP_HRD_ATM)
.protocol_type(0x0805);
assert_eq!(arp.opcode_value(), 0x1234);
assert_eq!(arp.hardware_type_value(), super::ARP_HRD_ATM);
assert_eq!(arp.protocol_type_value(), 0x0805);
let frame = Ethernet::new().src(src_mac()) / arp;
let bytes = frame.compile().unwrap().as_bytes().to_vec();
let decoded = Packet::decode_from_link(LinkType::Ethernet, &bytes).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.opcode_value(), 0x1234);
assert_eq!(decoded_arp.hardware_type_value(), super::ARP_HRD_ATM);
assert_eq!(decoded_arp.protocol_type_value(), 0x0805);
}
#[test]
fn arp_decode_variable_hardware_length_preserves_bytes() {
let sender_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x10, 0xaa, 0xbb];
let target_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x20, 0xcc, 0xdd];
let sender_pa = vec![192, 0, 2, 10];
let target_pa = vec![192, 0, 2, 1];
let mut bytes = vec![
0x00, 0x20, 0x08, 0x00, 0x08, 0x04, 0x00, 0x01, ];
bytes.extend_from_slice(&sender_hw);
bytes.extend_from_slice(&sender_pa);
bytes.extend_from_slice(&target_hw);
bytes.extend_from_slice(&target_pa);
let arp = decode_arp_layer(&bytes);
assert_eq!(arp.hardware_type_value(), super::ARP_HRD_INFINIBAND);
assert_eq!(arp.protocol_type_value(), super::ETHERTYPE_IPV4);
assert_eq!(arp.hardware_len_value(), 8);
assert_eq!(arp.protocol_len_value(), 4);
assert_eq!(arp.opcode_value(), ArpOperation::Request as u16);
assert_eq!(arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(arp.target_hardware_bytes_value(), target_hw);
assert_eq!(arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(arp.target_protocol_bytes_value(), target_pa);
assert_eq!(arp.sender_mac(), None);
assert_eq!(arp.target_mac(), None);
assert_eq!(arp.sender_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 10)));
assert_eq!(arp.target_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 1)));
let frame = Ethernet::new().src(src_mac()) / arp;
let recompiled =
Packet::decode_from_link(LinkType::Ethernet, frame.compile().unwrap().as_bytes()).unwrap();
assert_eq!(
recompiled
.layer::<Arp>()
.unwrap()
.sender_hardware_bytes_value(),
sender_hw
);
}
#[test]
fn arp_decode_variable_protocol_length_preserves_bytes() {
let sender_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x10];
let target_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x20];
let sender_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
];
let target_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02,
];
let mut bytes = vec![
0x00, 0x01, 0x86, 0xdd, 0x06, 0x10, 0x00, 0x02, ];
bytes.extend_from_slice(&sender_hw);
bytes.extend_from_slice(&sender_pa);
bytes.extend_from_slice(&target_hw);
bytes.extend_from_slice(&target_pa);
let arp = decode_arp_layer(&bytes);
assert_eq!(arp.hardware_len_value(), 6);
assert_eq!(arp.protocol_len_value(), 16);
assert_eq!(arp.protocol_type_value(), 0x86dd);
assert_eq!(arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(arp.target_protocol_bytes_value(), target_pa);
assert_eq!(
arp.sender_mac().map(|m| m.octets().to_vec()),
Some(sender_hw)
);
assert_eq!(
arp.target_mac().map(|m| m.octets().to_vec()),
Some(target_hw)
);
assert_eq!(arp.sender_ipv4(), None);
assert_eq!(arp.target_ipv4(), None);
}
#[test]
fn arp_decode_unknown_type_combination_preserves_fields() {
let sender_hw = vec![0xde, 0xad, 0xbe];
let target_hw = vec![0xca, 0xfe, 0x99];
let sender_pa = vec![0x11, 0x22];
let target_pa = vec![0x33, 0x44];
let mut bytes = vec![
0xab, 0xcd, 0x12, 0x34, 0x03, 0x02, 0x04, 0x00, ];
bytes.extend_from_slice(&sender_hw);
bytes.extend_from_slice(&sender_pa);
bytes.extend_from_slice(&target_hw);
bytes.extend_from_slice(&target_pa);
let arp = decode_arp_layer(&bytes);
assert_eq!(arp.hardware_type_value(), 0xabcd);
assert_eq!(arp.protocol_type_value(), 0x1234);
assert_eq!(arp.hardware_len_value(), 3);
assert_eq!(arp.protocol_len_value(), 2);
assert_eq!(arp.opcode_value(), 1024);
assert_eq!(arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(arp.target_hardware_bytes_value(), target_hw);
assert_eq!(arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(arp.target_protocol_bytes_value(), target_pa);
assert_eq!(arp.sender_mac(), None);
assert_eq!(arp.target_mac(), None);
assert_eq!(arp.sender_ipv4(), None);
assert_eq!(arp.target_ipv4(), None);
assert_eq!(ArpOperation::from_opcode(1024), None);
}
#[test]
fn arp_decode_zero_length_addresses_does_not_overflow() {
let bytes = vec![
0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, ];
let arp = decode_arp_layer(&bytes);
assert_eq!(arp.hardware_len_value(), 0);
assert_eq!(arp.protocol_len_value(), 0);
assert!(arp.sender_hardware_bytes_value().is_empty());
assert!(arp.sender_protocol_bytes_value().is_empty());
assert!(arp.target_hardware_bytes_value().is_empty());
assert!(arp.target_protocol_bytes_value().is_empty());
assert_eq!(arp.sender_mac(), None);
assert_eq!(arp.sender_ipv4(), None);
}
#[test]
fn arp_decode_truncated_header_returns_structured_error() {
let bytes = [0x00, 0x01, 0x08, 0x00, 0x06]; let err = Packet::decode_from_link(LinkType::Ethernet, arp_frame(&bytes)).unwrap_err();
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "arp header");
assert_eq!(required, 8);
assert_eq!(available, 5);
}
other => panic!("expected a structured truncation error, got {other:?}"),
}
}
#[test]
fn arp_decode_truncated_address_fields_returns_structured_error() {
let bytes = vec![
0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xaa, 0xbb, 0xcc, ];
let err = Packet::decode_from_link(LinkType::Ethernet, arp_frame(&bytes)).unwrap_err();
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "arp addresses");
assert_eq!(required, 28);
assert_eq!(available, 11);
}
other => panic!("expected a structured truncation error, got {other:?}"),
}
}
#[test]
fn arp_variable_lengths_decode_round_trips_byte_exact() {
let sender_hw = vec![0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11];
let sender_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
];
let target_hw = vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
let target_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02,
];
let arp = Arp::new()
.hardware_type(super::ARP_HRD_INFINIBAND)
.protocol_type(0x86dd)
.opcode(0x0fa0)
.sender_hardware(sender_hw.clone())
.sender_protocol(sender_pa.clone())
.target_hardware(target_hw.clone())
.target_protocol(target_pa.clone());
let frame = Ethernet::new().src(src_mac()) / arp;
let bytes = frame.compile().unwrap().as_bytes().to_vec();
let decoded = Packet::decode_from_link(LinkType::Ethernet, &bytes).unwrap();
let decoded_arp = decoded.layer::<Arp>().unwrap();
assert_eq!(decoded_arp.hardware_type_value(), super::ARP_HRD_INFINIBAND);
assert_eq!(decoded_arp.protocol_type_value(), 0x86dd);
assert_eq!(decoded_arp.hardware_len_value(), 8);
assert_eq!(decoded_arp.protocol_len_value(), 16);
assert_eq!(decoded_arp.opcode_value(), 0x0fa0);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(decoded_arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(decoded_arp.target_hardware_bytes_value(), target_hw);
assert_eq!(decoded_arp.target_protocol_bytes_value(), target_pa);
assert_eq!(decoded_arp.sender_mac(), None);
assert_eq!(decoded_arp.target_mac(), None);
assert_eq!(decoded_arp.sender_ipv4(), None);
assert_eq!(decoded_arp.target_ipv4(), None);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_slice());
}
#[test]
fn arp_variable_zero_protocol_length_with_ipv4_type_returns_none() {
let bytes = vec![
0x00, 0x01, 0x08, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x5e, 0x00, 0x53, 0x10, 0x00, 0x00, 0x5e, 0x00, 0x53,
0x20, ];
let arp = decode_arp_layer(&bytes);
assert_eq!(arp.protocol_type_value(), super::ETHERTYPE_IPV4);
assert_eq!(arp.protocol_len_value(), 0);
assert!(arp.sender_mac().is_some());
assert_eq!(arp.sender_ipv4(), None);
assert_eq!(arp.target_ipv4(), None);
}
const ARP_TRAILER: &[u8] = b"trailing-after-arp";
fn arp_body_ipv4_request() -> Vec<u8> {
vec![
0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x02, 0x00, 0x5e, 0x00, 0x53, 0x01, 0xc0, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x02, 0x01, ]
}
#[test]
fn arp_trailing_bytes_decode_as_raw_after_arp_layer() {
let mut body = arp_body_ipv4_request();
body.extend_from_slice(ARP_TRAILER);
let frame = arp_frame(&body);
let decoded = Packet::decode_from_link(LinkType::Ethernet, &frame)
.expect("complete ARP body with a trailer must decode");
let arp = decoded
.layer::<Arp>()
.expect("decoded packet must carry an Arp layer");
assert_eq!(arp.opcode_value(), ArpOperation::Request as u16);
assert_eq!(arp.sender_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 10)));
assert_eq!(arp.target_ipv4(), Some(Ipv4Addr::new(192, 0, 2, 1)));
let raw = decoded
.layer::<Raw>()
.expect("trailing bytes must surface as a Raw layer");
assert_eq!(raw.as_bytes(), ARP_TRAILER);
}
#[test]
fn arp_trailing_bytes_recompile_preserves_payload_byte_exact() {
let mut body = arp_body_ipv4_request();
body.extend_from_slice(ARP_TRAILER);
let frame = arp_frame(&body);
let decoded = Packet::decode_from_link(LinkType::Ethernet, &frame).unwrap();
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_slice());
}
#[test]
fn raw_after_arp_preserved_with_nonstandard_address_lengths() {
let mut body = vec![
0x00, 0x19, 0x86, 0xdd, 0x02, 0x03, 0x04, 0x00, 0xaa, 0xbb, 0x01, 0x02, 0x03, 0xcc, 0xdd, 0x04, 0x05, 0x06, ];
let trailer: &[u8] = &[0xde, 0xad, 0xbe, 0xef];
body.extend_from_slice(trailer);
let frame = arp_frame(&body);
let decoded = Packet::decode_from_link(LinkType::Ethernet, &frame)
.expect("nonstandard but complete ARP body must decode");
let arp = decoded.layer::<Arp>().expect("expected an Arp layer");
assert_eq!(arp.hardware_len_value(), 2);
assert_eq!(arp.protocol_len_value(), 3);
assert_eq!(arp.opcode_value(), 0x0400);
let raw = decoded
.layer::<Raw>()
.expect("trailing bytes must surface as a Raw layer");
assert_eq!(raw.as_bytes(), trailer);
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_slice());
}
#[test]
fn raw_after_arp_absent_when_body_is_exact() {
let frame = arp_frame(&arp_body_ipv4_request());
let decoded = Packet::decode_from_link(LinkType::Ethernet, &frame).unwrap();
assert!(decoded.layer::<Arp>().is_some());
assert!(
decoded.layer::<Raw>().is_none(),
"an exact ARP body must not synthesize a Raw layer"
);
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_slice());
}
fn decode_arp_layer(body: &[u8]) -> Arp {
let frame = arp_frame(body);
let decoded = Packet::decode_from_link(LinkType::Ethernet, &frame)
.expect("structurally valid ARP body must decode");
decoded
.layer::<Arp>()
.expect("decoded packet must carry an Arp layer")
.clone()
}
fn arp_frame(body: &[u8]) -> Vec<u8> {
let mut frame = Vec::with_capacity(14 + body.len());
frame.extend_from_slice(&MacAddr::BROADCAST.octets());
frame.extend_from_slice(&src_mac().octets());
frame.extend_from_slice(ÐERTYPE_ARP.to_be_bytes());
frame.extend_from_slice(body);
frame
}
fn inspection_value(arp: &Arp, key: &str) -> String {
use crate::packet::Layer;
arp.inspection_fields()
.into_iter()
.find(|(name, _)| *name == key)
.map(|(_, value)| value)
.unwrap_or_else(|| panic!("expected inspection field `{key}`"))
}
#[test]
fn arp_summary_standard_ethernet_ipv4_stays_concise_and_readable() {
use crate::packet::Layer;
let reply = Arp::is_at(
Ipv4Addr::new(192, 0, 2, 1),
"02:00:5e:00:53:02".parse().unwrap(),
Ipv4Addr::new(192, 0, 2, 10),
"02:00:5e:00:53:01".parse().unwrap(),
);
assert_eq!(
reply.summary(),
"Arp(op=reply, psrc=192.0.2.1, pdst=192.0.2.10)"
);
let request = Arp::who_has(
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 1),
src_mac(),
);
assert_eq!(
request.summary(),
"Arp(op=request, psrc=192.0.2.10, pdst=192.0.2.1)"
);
}
#[test]
fn arp_summary_unknown_operation_and_nonstandard_protocol_stay_visible() {
use crate::packet::Layer;
let arp = Arp::new()
.opcode(0x0400)
.protocol_type(super::ETHERTYPE_IPV4)
.sender_protocol(vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08])
.target_protocol(vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]);
let summary = arp.summary();
assert!(summary.contains("op=1024"), "summary was {summary}");
assert!(
summary.contains("psrc=01 02 03 04 05 06 07 08"),
"summary was {summary}"
);
assert!(
summary.contains("pdst=11 22 33 44 55 66 77 88"),
"summary was {summary}"
);
}
#[test]
fn arp_inspection_standard_ethernet_ipv4_exposes_named_fields() {
let request = Arp::who_has(
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 1),
src_mac(),
);
assert_eq!(
inspection_value(&request, "hardware_type"),
"ethernet (0x0001)"
);
assert_eq!(inspection_value(&request, "protocol_type"), "ipv4 (0x0800)");
assert_eq!(inspection_value(&request, "hardware_len"), "6");
assert_eq!(inspection_value(&request, "protocol_len"), "4");
assert_eq!(inspection_value(&request, "operation"), "request (1)");
assert_eq!(
inspection_value(&request, "sender_hardware_addr"),
"02:00:5e:00:53:01"
);
assert_eq!(
inspection_value(&request, "sender_protocol_addr"),
"192.0.2.10"
);
assert_eq!(
inspection_value(&request, "target_protocol_addr"),
"192.0.2.1"
);
assert_eq!(
inspection_value(&request, "sender_hardware_bytes"),
"02 00 5e 00 53 01"
);
assert_eq!(
inspection_value(&request, "sender_protocol_bytes"),
"c0 00 02 0a"
);
}
#[test]
fn arp_inspection_nonstandard_values_remain_inspectable() {
let arp = Arp::new()
.hardware_type(0x4242)
.protocol_type(0x86dd)
.opcode(0x0400)
.sender_hardware(vec![0xaa, 0xbb])
.sender_protocol(vec![0x01, 0x02, 0x03])
.target_hardware(vec![0xcc, 0xdd])
.target_protocol(vec![0x04, 0x05, 0x06]);
assert_eq!(inspection_value(&arp, "hardware_type"), "0x4242");
assert_eq!(inspection_value(&arp, "protocol_type"), "0x86dd");
assert_eq!(inspection_value(&arp, "operation"), "1024");
assert_eq!(inspection_value(&arp, "hardware_len"), "2");
assert_eq!(inspection_value(&arp, "protocol_len"), "3");
assert_eq!(inspection_value(&arp, "sender_hardware_addr"), "aa bb");
assert_eq!(inspection_value(&arp, "sender_hardware_bytes"), "aa bb");
assert_eq!(inspection_value(&arp, "sender_protocol_addr"), "01 02 03");
assert_eq!(inspection_value(&arp, "sender_protocol_bytes"), "01 02 03");
assert_eq!(inspection_value(&arp, "target_protocol_bytes"), "04 05 06");
}
#[test]
fn arp_codepoint_known_operations_compile_decode_and_round_trip() {
use super::{
ARP_OP_ARP_NAK, ARP_OP_DRARP_ERROR, ARP_OP_DRARP_REPLY, ARP_OP_DRARP_REQUEST,
ARP_OP_INARP_REPLY, ARP_OP_INARP_REQUEST, ARP_OP_MAPOS_UNARP, ARP_OP_RARP_REPLY,
ARP_OP_RARP_REQUEST, ARP_OP_REPLY, ARP_OP_REQUEST,
};
use crate::packet::Layer;
let known = [
(ArpOperation::Request, ARP_OP_REQUEST, "request"),
(ArpOperation::Reply, ARP_OP_REPLY, "reply"),
(
ArpOperation::RarpRequest,
ARP_OP_RARP_REQUEST,
"rarp-request",
),
(ArpOperation::RarpReply, ARP_OP_RARP_REPLY, "rarp-reply"),
(
ArpOperation::DrarpRequest,
ARP_OP_DRARP_REQUEST,
"drarp-request",
),
(ArpOperation::DrarpReply, ARP_OP_DRARP_REPLY, "drarp-reply"),
(ArpOperation::DrarpError, ARP_OP_DRARP_ERROR, "drarp-error"),
(
ArpOperation::InArpRequest,
ARP_OP_INARP_REQUEST,
"inarp-request",
),
(ArpOperation::InArpReply, ARP_OP_INARP_REPLY, "inarp-reply"),
(ArpOperation::ArpNak, ARP_OP_ARP_NAK, "arp-nak"),
(ArpOperation::MaposUnarp, ARP_OP_MAPOS_UNARP, "mapos-unarp"),
];
for (operation, opcode, label) in known {
assert_eq!(operation.opcode(), opcode);
assert_eq!(ArpOperation::from_opcode(opcode), Some(operation));
assert_eq!(operation.label(), label);
let arp = Arp::new()
.operation(operation)
.sender_hardware(sender_mac().octets().to_vec())
.sender_protocol(Ipv4Addr::new(192, 0, 2, 1).octets().to_vec())
.target_hardware(src_mac().octets().to_vec())
.target_protocol(Ipv4Addr::new(192, 0, 2, 10).octets().to_vec());
assert_eq!(arp.opcode_value(), opcode);
assert!(
arp.summary().contains(&format!("op={label}")),
"summary for {label} was {}",
arp.summary()
);
let frame = (Ethernet::new().src(sender_mac()).dst(src_mac()) / arp.clone())
.compile()
.expect("known-codepoint ARP must compile");
let decoded = Packet::decode_from_link(LinkType::Ethernet, frame.as_bytes())
.expect("known-codepoint ARP must decode");
let decoded_arp = decoded
.layer::<Arp>()
.expect("decoded packet must carry an Arp layer");
assert_eq!(decoded_arp.opcode_value(), opcode);
assert_eq!(
ArpOperation::from_opcode(decoded_arp.opcode_value()),
Some(operation)
);
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_bytes());
}
}
#[test]
fn arp_unknown_codepoints_are_not_rejected() {
use super::{arp_hardware_type_label, arp_protocol_type_label};
use crate::packet::Layer;
let unknown_opcode: u16 = 0x0fa0; let unknown_hardware: u16 = 0x1234; let unknown_protocol: u16 = 0x88b5;
assert_eq!(ArpOperation::from_opcode(unknown_opcode), None);
assert_eq!(arp_hardware_type_label(unknown_hardware), None);
assert_eq!(arp_protocol_type_label(unknown_protocol), None);
let arp = Arp::new()
.hardware_type(unknown_hardware)
.protocol_type(unknown_protocol)
.opcode(unknown_opcode)
.sender_hardware(vec![0xaa, 0xbb, 0xcc])
.sender_protocol(vec![0x01, 0x02])
.target_hardware(vec![0xdd, 0xee, 0xff])
.target_protocol(vec![0x03, 0x04]);
assert_eq!(arp.hardware_type_value(), unknown_hardware);
assert_eq!(arp.protocol_type_value(), unknown_protocol);
assert_eq!(arp.opcode_value(), unknown_opcode);
assert!(
arp.summary().contains(&format!("op={unknown_opcode}")),
"summary was {}",
arp.summary()
);
assert_eq!(
inspection_value(&arp, "hardware_type"),
format!("0x{unknown_hardware:04x}")
);
assert_eq!(
inspection_value(&arp, "protocol_type"),
format!("0x{unknown_protocol:04x}")
);
assert_eq!(
inspection_value(&arp, "operation"),
format!("{unknown_opcode}")
);
let frame = (Ethernet::new().src(src_mac()) / arp)
.compile()
.expect("unknown ARP codepoints must compile, not be rejected");
let decoded = Packet::decode_from_link(LinkType::Ethernet, frame.as_bytes())
.expect("unknown ARP codepoints must decode, not be rejected");
let decoded_arp = decoded
.layer::<Arp>()
.expect("decoded packet must carry an Arp layer");
assert_eq!(decoded_arp.hardware_type_value(), unknown_hardware);
assert_eq!(decoded_arp.protocol_type_value(), unknown_protocol);
assert_eq!(decoded_arp.opcode_value(), unknown_opcode);
assert_eq!(decoded_arp.hardware_type_label(), None);
assert_eq!(decoded_arp.protocol_type_label(), None);
assert_eq!(ArpOperation::from_opcode(decoded_arp.opcode_value()), None);
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_bytes());
}
#[test]
fn arp_variable_length_nonstandard_hardware_and_protocol_build_compile_decode_inspect() {
let sender_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x10, 0xaa, 0xbb];
let target_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x20, 0xcc, 0xdd];
let sender_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
];
let target_pa = vec![
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02,
];
let arp = Arp::new()
.hardware_type(super::ARP_HRD_INFINIBAND)
.protocol_type(0x86dd)
.operation(ArpOperation::Request)
.sender_hardware(sender_hw.clone())
.sender_protocol(sender_pa.clone())
.target_hardware(target_hw.clone())
.target_protocol(target_pa.clone());
assert_eq!(arp.hardware_len_value(), 8);
assert_eq!(arp.protocol_len_value(), 16);
let frame = (Ethernet::new().src(src_mac()).dst(MacAddr::BROADCAST) / arp)
.compile()
.expect("nonstandard variable-length ARP must compile");
let decoded = Packet::decode_from_link(LinkType::Ethernet, frame.as_bytes())
.expect("nonstandard variable-length ARP must decode");
let decoded_arp = decoded
.layer::<Arp>()
.expect("decoded packet must carry an Arp layer");
assert_eq!(decoded_arp.hardware_type_value(), super::ARP_HRD_INFINIBAND);
assert_eq!(decoded_arp.protocol_type_value(), 0x86dd);
assert_eq!(decoded_arp.hardware_len_value(), 8);
assert_eq!(decoded_arp.protocol_len_value(), 16);
assert_eq!(decoded_arp.opcode_value(), ArpOperation::Request as u16);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(decoded_arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(decoded_arp.target_hardware_bytes_value(), target_hw);
assert_eq!(decoded_arp.target_protocol_bytes_value(), target_pa);
assert_eq!(decoded_arp.sender_mac(), None);
assert_eq!(decoded_arp.target_mac(), None);
assert_eq!(decoded_arp.sender_ipv4(), None);
assert_eq!(decoded_arp.target_ipv4(), None);
assert_eq!(inspection_value(decoded_arp, "hardware_len"), "8");
assert_eq!(inspection_value(decoded_arp, "protocol_len"), "16");
assert_eq!(
inspection_value(decoded_arp, "sender_hardware_addr"),
"00 00 5e 00 53 10 aa bb"
);
assert_eq!(
inspection_value(decoded_arp, "sender_hardware_bytes"),
"00 00 5e 00 53 10 aa bb"
);
assert_eq!(
inspection_value(decoded_arp, "target_protocol_bytes"),
"20 01 0d b8 00 00 00 00 00 00 00 00 00 00 00 02"
);
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_bytes());
}
#[test]
fn arp_variable_length_explicit_lengths_with_ipv4_type_decline_typed_ipv4() {
let sender_hw = vec![0xde, 0xad, 0xbe];
let target_hw = vec![0xca, 0xfe, 0x99];
let sender_pa = vec![0xc0, 0x00]; let target_pa = vec![0x02, 0x01];
let arp = Arp::new()
.protocol_type(super::ETHERTYPE_IPV4)
.hardware_len(3)
.protocol_len(2)
.operation(ArpOperation::Request)
.sender_hardware(sender_hw.clone())
.sender_protocol(sender_pa.clone())
.target_hardware(target_hw.clone())
.target_protocol(target_pa.clone());
let frame = (Ethernet::new().src(src_mac()).dst(MacAddr::BROADCAST) / arp)
.compile()
.expect("explicit nonstandard lengths must compile");
let decoded = Packet::decode_from_link(LinkType::Ethernet, frame.as_bytes())
.expect("explicit nonstandard lengths must decode");
let decoded_arp = decoded
.layer::<Arp>()
.expect("decoded packet must carry an Arp layer");
assert_eq!(decoded_arp.protocol_type_value(), super::ETHERTYPE_IPV4);
assert_eq!(decoded_arp.hardware_len_value(), 3);
assert_eq!(decoded_arp.protocol_len_value(), 2);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(decoded_arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(decoded_arp.target_hardware_bytes_value(), target_hw);
assert_eq!(decoded_arp.target_protocol_bytes_value(), target_pa);
assert_eq!(decoded_arp.sender_mac(), None);
assert_eq!(decoded_arp.target_mac(), None);
assert_eq!(decoded_arp.sender_ipv4(), None);
assert_eq!(decoded_arp.target_ipv4(), None);
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_bytes());
}
#[test]
fn arp_raw_address_nonstandard_lengths_round_trip_byte_exact_and_decline_typed() {
let sender_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x30, 0x77];
let target_hw = vec![0x00, 0x00, 0x5e, 0x00, 0x53, 0x40, 0x88];
let sender_pa = vec![0x20, 0x01, 0x0d, 0xb8, 0x05];
let target_pa = vec![0x20, 0x01, 0x0d, 0xb8, 0x06];
let arp = Arp::new()
.hardware_type(super::ARP_HRD_ATM)
.protocol_type(0x86dd)
.opcode(0x0fa0) .sender_hardware_bytes(sender_hw.clone())
.target_hardware_bytes(target_hw.clone())
.sender_protocol_bytes(sender_pa.clone())
.target_protocol_bytes(target_pa.clone())
.hardware_len(7)
.protocol_len(5);
let frame = (Ethernet::new().src(src_mac()).dst(MacAddr::BROADCAST) / arp)
.compile()
.expect("raw nonstandard-length ARP must compile");
let decoded = Packet::decode_from_link(LinkType::Ethernet, frame.as_bytes())
.expect("raw nonstandard-length ARP must decode");
let decoded_arp = decoded
.layer::<Arp>()
.expect("decoded packet must carry an Arp layer");
assert_eq!(decoded_arp.hardware_type_value(), super::ARP_HRD_ATM);
assert_eq!(decoded_arp.protocol_type_value(), 0x86dd);
assert_eq!(decoded_arp.hardware_len_value(), 7);
assert_eq!(decoded_arp.protocol_len_value(), 5);
assert_eq!(decoded_arp.opcode_value(), 0x0fa0);
assert_eq!(decoded_arp.sender_hardware_bytes_value(), sender_hw);
assert_eq!(decoded_arp.target_hardware_bytes_value(), target_hw);
assert_eq!(decoded_arp.sender_protocol_bytes_value(), sender_pa);
assert_eq!(decoded_arp.target_protocol_bytes_value(), target_pa);
assert_eq!(decoded_arp.sender_mac(), None);
assert_eq!(decoded_arp.target_mac(), None);
assert_eq!(decoded_arp.sender_ipv4(), None);
assert_eq!(decoded_arp.target_ipv4(), None);
assert_eq!(decoded.compile().unwrap().as_bytes(), frame.as_bytes());
}