pub mod mac_addres;
use mac_addres::MacAddress;
use serde::Serialize;
pub mod ethertype;
pub mod vlan_tag;
use crate::{
checks::data_link::validate_data_link_length, errors::data_link::DataLinkError,
parse::data_link::vlan_tag::VlanTag,
};
use ethertype::Ethertype;
#[derive(Debug, Clone, Serialize, Eq)]
pub struct DataLink<'a> {
pub destination_mac: String,
pub source_mac: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub vlan: Option<VlanTag>,
pub ethertype: String,
#[serde(skip_serializing)]
pub payload: &'a [u8],
}
impl<'a> TryFrom<&'a [u8]> for DataLink<'a> {
type Error = DataLinkError;
fn try_from(packets: &'a [u8]) -> Result<Self, Self::Error> {
validate_data_link_length(packets)?;
let destination_mac = MacAddress::try_from(&packets[0..6])?;
let source_mac = MacAddress::try_from(&packets[6..12])?;
let raw_ethertype = u16::from_be_bytes([packets[12], packets[13]]);
let mut vlan: Option<VlanTag> = None;
let ethertype: String;
let payload: &'a [u8];
if raw_ethertype == 0x8100 {
if packets.len() < 18 {
return Err(DataLinkError::DataLinkTooShort(packets.len() as u8));
}
let vlan_tag = VlanTag::try_from(&packets[14..18])?;
ethertype = vlan_tag.inner_ethertype_name().to_string();
payload = &packets[18..];
vlan = Some(vlan_tag);
} else {
ethertype = Ethertype::from(raw_ethertype).name().to_string();
payload = &packets[14..];
}
Ok(DataLink {
destination_mac: destination_mac.to_string(),
source_mac: source_mac.to_string(),
vlan,
ethertype,
payload,
})
}
}
impl<'a> PartialEq for DataLink<'a> {
fn eq(&self, other: &Self) -> bool {
self.destination_mac == other.destination_mac
&& self.source_mac == other.source_mac
&& self.vlan == other.vlan
&& self.ethertype == other.ethertype
}
}
use std::hash::{Hash, Hasher};
impl<'a> Hash for DataLink<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.destination_mac.hash(state);
self.source_mac.hash(state);
self.vlan.hash(state);
self.ethertype.hash(state);
}
}
#[cfg(test)]
mod tests {
use crate::errors::data_link::DataLinkError;
use crate::parse::data_link::DataLink;
use crate::parse::data_link::mac_addres::MacAddress;
#[test]
fn test_datalink_try_from_valid_packet() {
let raw_packet: [u8; 18] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, ];
let datalink =
DataLink::try_from(raw_packet.as_ref()).expect("Failed to parse valid packet");
assert_eq!(
datalink.destination_mac,
MacAddress::try_from(&raw_packet[0..6])
.unwrap()
.display_with_oui()
);
assert_eq!(
datalink.source_mac,
MacAddress::try_from(&raw_packet[6..12])
.unwrap()
.display_with_oui()
);
assert_eq!(datalink.ethertype, "IPv4"); }
#[test]
fn test_datalink_try_from_invalid_length() {
let short_packet: [u8; 10] = [0x00; 10];
let result = DataLink::try_from(short_packet.as_ref());
assert!(matches!(result, Err(DataLinkError::DataLinkTooShort(_))));
}
#[test]
fn test_datalink_try_from_ethertype_parsing() {
let raw_packet: [u8; 18] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x86, 0xDD, 0x60, 0x00, 0x00, 0x00, ];
let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
assert_eq!(datalink.ethertype, "IPv6"); }
#[test]
fn test_datalink_try_from_ethertype_unknown() {
let raw_packet: [u8; 18] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xAB, 0xCD, 0x12, 0x34, 0x56, 0x78, ];
let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
assert_eq!(datalink.ethertype, "Unknown (0xABCD)"); }
#[test]
fn test_datalink_try_from_empty_payload() {
let raw_packet: [u8; 14] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xAB, 0xCD, ];
let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
assert_eq!(datalink.ethertype, "Unknown (0xABCD)"); }
#[test]
fn test_datalink_try_from_vlan_tagged() {
let raw_packet: [u8; 22] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x81, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, ];
let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
assert_eq!(datalink.ethertype, "IPv4");
assert!(datalink.vlan.is_some());
let vlan = datalink.vlan.unwrap();
assert_eq!(vlan.id, 10);
assert_eq!(datalink.payload, &raw_packet[18..]);
}
}