packet_parser/parse/data_link/
mod.rs1pub mod mac_addres;
51use mac_addres::MacAddress;
52use serde::Serialize;
53
54pub mod ethertype;
55pub mod vlan_tag;
56
57use crate::{
58 checks::data_link::validate_data_link_length, errors::data_link::DataLinkError,
59 parse::data_link::vlan_tag::VlanTag,
60};
61
62use ethertype::Ethertype;
63
64#[derive(Debug, Clone, Serialize, Eq)]
67pub struct DataLink<'a> {
68 pub destination_mac: String,
70 pub source_mac: String,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub vlan: Option<VlanTag>,
74 pub ethertype: String,
76 #[serde(skip_serializing)]
78 pub payload: &'a [u8],
79}
80
81impl<'a> TryFrom<&'a [u8]> for DataLink<'a> {
82 type Error = DataLinkError;
83
84 fn try_from(packets: &'a [u8]) -> Result<Self, Self::Error> {
85 validate_data_link_length(packets)?;
86
87 let destination_mac = MacAddress::try_from(&packets[0..6])?;
88 let source_mac = MacAddress::try_from(&packets[6..12])?;
89
90 let raw_ethertype = u16::from_be_bytes([packets[12], packets[13]]);
92
93 let mut vlan: Option<VlanTag> = None;
94 let ethertype: String;
95 let payload: &'a [u8];
96
97 if raw_ethertype == 0x8100 {
98 if packets.len() < 18 {
101 return Err(DataLinkError::DataLinkTooShort(packets.len() as u8));
102 }
103
104 let vlan_tag = VlanTag::try_from(&packets[14..18])?;
106
107 ethertype = vlan_tag.inner_ethertype_name().to_string();
108 payload = &packets[18..];
109 vlan = Some(vlan_tag);
110 } else {
111 ethertype = Ethertype::from(raw_ethertype).name().to_string();
113 payload = &packets[14..];
114 }
115
116 Ok(DataLink {
117 destination_mac: destination_mac.display_with_oui(),
118 source_mac: source_mac.display_with_oui(),
119 vlan,
120 ethertype,
121 payload,
122 })
123 }
124}
125
126impl<'a> PartialEq for DataLink<'a> {
127 fn eq(&self, other: &Self) -> bool {
128 self.destination_mac == other.destination_mac
129 && self.source_mac == other.source_mac
130 && self.vlan == other.vlan
131 && self.ethertype == other.ethertype
132 }
133}
134
135use std::hash::{Hash, Hasher};
136
137impl<'a> Hash for DataLink<'a> {
138 fn hash<H: Hasher>(&self, state: &mut H) {
139 self.destination_mac.hash(state);
140 self.source_mac.hash(state);
141 self.vlan.hash(state);
142 self.ethertype.hash(state);
143 }
144}
145
146#[cfg(test)]
147mod tests {
148
149 use crate::errors::data_link::DataLinkError;
150 use crate::parse::data_link::DataLink;
151 use crate::parse::data_link::mac_addres::MacAddress;
152
153 #[test]
154 fn test_datalink_try_from_valid_packet() {
155 let raw_packet: [u8; 18] = [
156 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, ];
161
162 let datalink =
163 DataLink::try_from(raw_packet.as_ref()).expect("Failed to parse valid packet");
164
165 assert_eq!(
166 datalink.destination_mac,
167 MacAddress::try_from(&raw_packet[0..6])
168 .unwrap()
169 .display_with_oui()
170 );
171 assert_eq!(
172 datalink.source_mac,
173 MacAddress::try_from(&raw_packet[6..12])
174 .unwrap()
175 .display_with_oui()
176 );
177 assert_eq!(datalink.ethertype, "IPv4"); }
179
180 #[test]
181 fn test_datalink_try_from_invalid_length() {
182 let short_packet: [u8; 10] = [0x00; 10];
183
184 let result = DataLink::try_from(short_packet.as_ref());
185 assert!(matches!(result, Err(DataLinkError::DataLinkTooShort(_))));
186 }
187
188 #[test]
189 fn test_datalink_try_from_ethertype_parsing() {
190 let raw_packet: [u8; 18] = [
191 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x86, 0xDD, 0x60, 0x00, 0x00, 0x00, ];
196
197 let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
198 assert_eq!(datalink.ethertype, "IPv6"); }
200
201 #[test]
202 fn test_datalink_try_from_ethertype_unknown() {
203 let raw_packet: [u8; 18] = [
204 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xAB, 0xCD, 0x12, 0x34, 0x56, 0x78, ];
209
210 let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
211 assert_eq!(datalink.ethertype, "Unknown (0xABCD)"); }
213 #[test]
214 fn test_datalink_try_from_empty_payload() {
215 let raw_packet: [u8; 14] = [
216 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xAB, 0xCD, ];
220
221 let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
222 assert_eq!(datalink.ethertype, "Unknown (0xABCD)"); }
224
225 #[test]
226 fn test_datalink_try_from_vlan_tagged() {
227 let raw_packet: [u8; 22] = [
229 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x81, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, ];
236
237 let datalink = DataLink::try_from(raw_packet.as_ref()).unwrap();
238
239 assert_eq!(datalink.ethertype, "IPv4");
240 assert!(datalink.vlan.is_some());
241 let vlan = datalink.vlan.unwrap();
242 assert_eq!(vlan.id, 10);
243 assert_eq!(datalink.payload, &raw_packet[18..]);
244 }
245}