1use smallvec::SmallVec;
8
9use super::{FieldValue, ParseContext, ParseResult, Protocol};
10use crate::schema::{DataKind, FieldDescriptor};
11
12pub const LINKTYPE_LINUX_SLL: u16 = 113;
14
15pub const LINUX_SLL_HEADER_LEN: usize = 16;
17
18pub mod arphrd {
20 pub const ETHER: u16 = 1;
21 pub const LOOPBACK: u16 = 772;
22 pub const FRAD: u16 = 770;
23 pub const IPGRE: u16 = 778;
24 pub const IEEE80211_RADIOTAP: u16 = 803;
25 pub const IP6GRE: u16 = 823;
26 pub const NETLINK: u16 = 824;
27}
28
29pub mod packet_type {
31 pub const HOST: u16 = 0; pub const BROADCAST: u16 = 1; pub const MULTICAST: u16 = 2; pub const OTHERHOST: u16 = 3; pub const OUTGOING: u16 = 4; }
37
38fn packet_type_name(pkt_type: u16) -> &'static str {
40 match pkt_type {
41 packet_type::HOST => "HOST",
42 packet_type::BROADCAST => "BROADCAST",
43 packet_type::MULTICAST => "MULTICAST",
44 packet_type::OTHERHOST => "OTHERHOST",
45 packet_type::OUTGOING => "OUTGOING",
46 _ => "UNKNOWN",
47 }
48}
49
50fn arphrd_name(arphrd: u16) -> &'static str {
52 match arphrd {
53 arphrd::ETHER => "ETHER",
54 arphrd::LOOPBACK => "LOOPBACK",
55 arphrd::FRAD => "FRAD",
56 arphrd::IPGRE => "IPGRE",
57 arphrd::IEEE80211_RADIOTAP => "IEEE80211_RADIOTAP",
58 arphrd::IP6GRE => "IP6GRE",
59 arphrd::NETLINK => "NETLINK",
60 _ => "UNKNOWN",
61 }
62}
63
64#[derive(Debug, Clone, Copy)]
69pub struct LinuxSllProtocol;
70
71impl Protocol for LinuxSllProtocol {
72 fn name(&self) -> &'static str {
73 "linux_sll"
74 }
75
76 fn display_name(&self) -> &'static str {
77 "Linux SLL"
78 }
79
80 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
81 if context.is_root() && context.link_type == LINKTYPE_LINUX_SLL {
83 return Some(100);
84 }
85 None
86 }
87
88 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
89 if data.len() < LINUX_SLL_HEADER_LEN {
91 return ParseResult::error(
92 format!("Linux SLL header too short: {} bytes", data.len()),
93 data,
94 );
95 }
96
97 let mut fields = SmallVec::new();
98
99 let pkt_type = u16::from_be_bytes([data[0], data[1]]);
101 let arphrd_type = u16::from_be_bytes([data[2], data[3]]);
102 let addr_len = u16::from_be_bytes([data[4], data[5]]);
103 let protocol = u16::from_be_bytes([data[14], data[15]]);
105
106 fields.push(("packet_type", FieldValue::UInt16(pkt_type)));
107 fields.push((
108 "packet_type_name",
109 FieldValue::Str(packet_type_name(pkt_type)),
110 ));
111 fields.push(("arphrd_type", FieldValue::UInt16(arphrd_type)));
112 fields.push(("arphrd_name", FieldValue::Str(arphrd_name(arphrd_type))));
113 fields.push(("addr_len", FieldValue::UInt16(addr_len)));
114
115 let valid_addr_len = (addr_len as usize).min(8);
117 if valid_addr_len > 0 {
118 let addr_slice = &data[6..6 + valid_addr_len];
119 fields.push(("addr", FieldValue::Bytes(addr_slice)));
120 }
121
122 fields.push(("protocol", FieldValue::UInt16(protocol)));
123
124 let remaining = &data[LINUX_SLL_HEADER_LEN..];
126
127 let mut child_hints = SmallVec::new();
129
130 match arphrd_type {
131 arphrd::NETLINK => {
132 child_hints.push(("sll_arphrd", arphrd::NETLINK as u64));
134 child_hints.push(("netlink_family", protocol as u64));
135 child_hints.push(("is_netlink", 1u64));
136 }
137 arphrd::ETHER | arphrd::LOOPBACK => {
138 child_hints.push(("sll_arphrd", arphrd_type as u64));
140 child_hints.push(("ethertype", protocol as u64));
141 }
142 _ => {
143 child_hints.push(("sll_arphrd", arphrd_type as u64));
144 child_hints.push(("sll_protocol", protocol as u64));
145 }
146 }
147
148 ParseResult::success(fields, remaining, child_hints)
149 }
150
151 fn schema_fields(&self) -> Vec<FieldDescriptor> {
152 vec![
153 FieldDescriptor::new("linux_sll.packet_type", DataKind::UInt16).set_nullable(true),
154 FieldDescriptor::new("linux_sll.packet_type_name", DataKind::String).set_nullable(true),
155 FieldDescriptor::new("linux_sll.arphrd_type", DataKind::UInt16).set_nullable(true),
156 FieldDescriptor::new("linux_sll.arphrd_name", DataKind::String).set_nullable(true),
157 FieldDescriptor::new("linux_sll.addr_len", DataKind::UInt16).set_nullable(true),
158 FieldDescriptor::new("linux_sll.addr", DataKind::Binary).set_nullable(true),
159 FieldDescriptor::new("linux_sll.protocol", DataKind::UInt16).set_nullable(true),
160 ]
161 }
162
163 fn child_protocols(&self) -> &[&'static str] {
164 &["netlink", "ethernet", "ipv4", "ipv6"]
165 }
166
167 fn dependencies(&self) -> &'static [&'static str] {
168 &[] }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 fn create_sll_header(
178 pkt_type: u16,
179 arphrd: u16,
180 addr_len: u16,
181 addr: [u8; 8],
182 protocol: u16,
183 ) -> Vec<u8> {
184 let mut header = Vec::with_capacity(16);
185 header.extend_from_slice(&pkt_type.to_be_bytes());
186 header.extend_from_slice(&arphrd.to_be_bytes());
187 header.extend_from_slice(&addr_len.to_be_bytes());
188 header.extend_from_slice(&addr);
189 header.extend_from_slice(&protocol.to_be_bytes());
190 header
191 }
192
193 #[test]
197 fn test_can_parse_linux_sll_at_root() {
198 let parser = LinuxSllProtocol;
199 let ctx = ParseContext::new(LINKTYPE_LINUX_SLL);
200
201 assert!(parser.can_parse(&ctx).is_some());
202 assert_eq!(parser.can_parse(&ctx), Some(100));
203 }
204
205 #[test]
209 fn test_cannot_parse_ethernet() {
210 let parser = LinuxSllProtocol;
211 let ctx = ParseContext::new(1); assert!(parser.can_parse(&ctx).is_none());
214 }
215
216 #[test]
220 fn test_cannot_parse_when_not_root() {
221 let parser = LinuxSllProtocol;
222 let mut ctx = ParseContext::new(LINKTYPE_LINUX_SLL);
223 ctx.parent_protocol = Some("something");
224
225 assert!(parser.can_parse(&ctx).is_none());
226 }
227
228 #[test]
232 fn test_parse_sll_ethernet() {
233 let header = create_sll_header(
234 packet_type::HOST,
235 arphrd::ETHER,
236 6,
237 [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x00, 0x00],
238 0x0800, );
240 let parser = LinuxSllProtocol;
241 let ctx = ParseContext::new(LINKTYPE_LINUX_SLL);
242
243 let result = parser.parse(&header, &ctx);
244
245 assert!(result.is_ok());
246 assert_eq!(result.get("packet_type"), Some(&FieldValue::UInt16(0)));
247 assert_eq!(
248 result.get("packet_type_name"),
249 Some(&FieldValue::Str("HOST"))
250 );
251 assert_eq!(
252 result.get("arphrd_type"),
253 Some(&FieldValue::UInt16(arphrd::ETHER))
254 );
255 assert_eq!(result.get("arphrd_name"), Some(&FieldValue::Str("ETHER")));
256 assert_eq!(result.get("protocol"), Some(&FieldValue::UInt16(0x0800)));
257 }
258
259 #[test]
263 fn test_parse_sll_netlink() {
264 let header = create_sll_header(
265 packet_type::OUTGOING,
266 arphrd::NETLINK,
267 0,
268 [0; 8],
269 0, );
271 let parser = LinuxSllProtocol;
272 let ctx = ParseContext::new(LINKTYPE_LINUX_SLL);
273
274 let result = parser.parse(&header, &ctx);
275
276 assert!(result.is_ok());
277 assert_eq!(
278 result.get("arphrd_type"),
279 Some(&FieldValue::UInt16(arphrd::NETLINK))
280 );
281 assert_eq!(result.get("arphrd_name"), Some(&FieldValue::Str("NETLINK")));
282
283 assert!(result
285 .child_hints
286 .iter()
287 .any(|(k, v)| *k == "is_netlink" && *v == 1));
288 assert!(result
289 .child_hints
290 .iter()
291 .any(|(k, v)| *k == "netlink_family" && *v == 0));
292 }
293
294 #[test]
298 fn test_parse_sll_header_too_short() {
299 let short_data = vec![0u8; 10]; let parser = LinuxSllProtocol;
301 let ctx = ParseContext::new(LINKTYPE_LINUX_SLL);
302
303 let result = parser.parse(&short_data, &ctx);
304
305 assert!(!result.is_ok());
306 assert!(result.error.is_some());
307 }
308
309 #[test]
313 fn test_packet_type_names() {
314 assert_eq!(packet_type_name(packet_type::HOST), "HOST");
315 assert_eq!(packet_type_name(packet_type::BROADCAST), "BROADCAST");
316 assert_eq!(packet_type_name(packet_type::MULTICAST), "MULTICAST");
317 assert_eq!(packet_type_name(packet_type::OTHERHOST), "OTHERHOST");
318 assert_eq!(packet_type_name(packet_type::OUTGOING), "OUTGOING");
319 assert_eq!(packet_type_name(99), "UNKNOWN");
320 }
321
322 #[test]
326 fn test_arphrd_names() {
327 assert_eq!(arphrd_name(arphrd::ETHER), "ETHER");
328 assert_eq!(arphrd_name(arphrd::NETLINK), "NETLINK");
329 assert_eq!(arphrd_name(arphrd::LOOPBACK), "LOOPBACK");
330 assert_eq!(arphrd_name(999), "UNKNOWN");
331 }
332
333 #[test]
337 fn test_linux_sll_schema_fields() {
338 let parser = LinuxSllProtocol;
339 let fields = parser.schema_fields();
340
341 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
342
343 assert!(field_names.contains(&"linux_sll.packet_type"));
344 assert!(field_names.contains(&"linux_sll.packet_type_name"));
345 assert!(field_names.contains(&"linux_sll.arphrd_type"));
346 assert!(field_names.contains(&"linux_sll.arphrd_name"));
347 assert!(field_names.contains(&"linux_sll.addr_len"));
348 assert!(field_names.contains(&"linux_sll.addr"));
349 assert!(field_names.contains(&"linux_sll.protocol"));
350 }
351
352 #[test]
356 fn test_linux_sll_child_protocols() {
357 let parser = LinuxSllProtocol;
358 let children = parser.child_protocols();
359
360 assert!(children.contains(&"netlink"));
361 assert!(children.contains(&"ethernet"));
362 }
363
364 #[test]
368 fn test_linux_sll_no_dependencies() {
369 let parser = LinuxSllProtocol;
370 let deps = parser.dependencies();
371
372 assert!(deps.is_empty());
373 }
374
375 #[test]
379 fn test_remaining_data() {
380 let mut data = create_sll_header(packet_type::HOST, arphrd::NETLINK, 0, [0; 8], 0);
381 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]);
383
384 let parser = LinuxSllProtocol;
385 let ctx = ParseContext::new(LINKTYPE_LINUX_SLL);
386
387 let result = parser.parse(&data, &ctx);
388
389 assert!(result.is_ok());
390 assert_eq!(result.remaining.len(), 4);
391 assert_eq!(result.remaining, &[0x01, 0x02, 0x03, 0x04]);
392 }
393}