use crate::{
checks::application::dhcpv6::{validate_dhcpv6_message_type, validate_dhcpv6_min_length},
errors::application::dhcpv6::Dhcpv6PacketParseError,
};
use std::fmt;
#[cfg_attr(doc, aquamarine::aquamarine)]
#[derive(Debug, PartialEq)]
pub struct Dhcpv6Packet<'a> {
pub message_type: u8,
pub transaction_id: u32,
pub options: &'a [u8],
}
impl<'a> fmt::Display for Dhcpv6Packet<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"DHCPv6 Packet: message_type={}, transaction_id={:06X}, options={:02X?}",
self.message_type, self.transaction_id, self.options
)
}
}
impl<'a> TryFrom<&'a [u8]> for Dhcpv6Packet<'a> {
type Error = Dhcpv6PacketParseError;
fn try_from(payload: &'a [u8]) -> Result<Self, Self::Error> {
validate_dhcpv6_min_length(payload)?;
let message_type = payload[0];
validate_dhcpv6_message_type(message_type)?;
let transaction_id = u32::from_be_bytes([0, payload[1], payload[2], payload[3]]);
let options = &payload[4..];
Ok(Dhcpv6Packet {
message_type,
transaction_id,
options,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_dhcpv6_packet() {
let payload = vec![
0x01, 0x12, 0x34, 0x56, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x03, 0x00, 0x01, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
];
match Dhcpv6Packet::try_from(payload.as_slice()) {
Ok(packet) => {
assert_eq!(packet.message_type, 1);
assert_eq!(packet.transaction_id, 0x123456);
assert_eq!(
packet.options,
&[
0x00, 0x01, 0x00, 0x0A, 0x00, 0x03, 0x00, 0x01, 0x00, 0x11, 0x22, 0x33,
0x44, 0x55
]
);
}
Err(_) => panic!("Expected valid DHCPv6 packet"),
}
}
#[test]
fn test_parse_dhcpv6_packet_short_payload() {
let short_payload = vec![0x01, 0x12, 0x34]; match Dhcpv6Packet::try_from(short_payload.as_slice()) {
Ok(_) => panic!("Expected invalid DHCPv6 packet due to short payload"),
Err(e) => assert_eq!(e, Dhcpv6PacketParseError::PacketLength),
}
}
#[test]
fn test_parse_dhcpv6_packet_invalid_type() {
let invalid_payload = vec![
0x0E, 0x12, 0x34, 0x56, 0x00, 0x00, 0x00, 0x00, ];
match Dhcpv6Packet::try_from(invalid_payload.as_slice()) {
Ok(_) => panic!("Expected invalid DHCPv6 packet due to invalid message type"),
Err(e) => assert_eq!(e, Dhcpv6PacketParseError::MessageType { message_type: 14 }),
}
}
#[test]
fn test_parse_dhcpv6_relay_agent() {
let relay_payload = vec![
0x0C, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, ];
match Dhcpv6Packet::try_from(relay_payload.as_slice()) {
Ok(packet) => {
assert_eq!(packet.message_type, 12);
}
Err(e) => panic!("Expected valid DHCPv6 Relay packet, got {:?}", e),
}
}
}