1use smallvec::SmallVec;
4
5use super::ethernet::ethertype;
6use super::{FieldValue, ParseContext, ParseResult, Protocol};
7use crate::schema::{DataKind, FieldDescriptor};
8
9pub mod operation {
11 pub const REQUEST: u16 = 1;
12 pub const REPLY: u16 = 2;
13}
14
15#[derive(Debug, Clone, Copy)]
17pub struct ArpProtocol;
18
19impl Protocol for ArpProtocol {
20 fn name(&self) -> &'static str {
21 "arp"
22 }
23
24 fn display_name(&self) -> &'static str {
25 "ARP"
26 }
27
28 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
29 match context.hint("ethertype") {
30 Some(et) if et == ethertype::ARP as u64 => Some(100),
31 _ => None,
32 }
33 }
34
35 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
36 if data.len() < 28 {
38 return ParseResult::error(format!("ARP packet too short: {} bytes", data.len()), data);
39 }
40
41 let mut fields = SmallVec::new();
42
43 let hardware_type = u16::from_be_bytes([data[0], data[1]]);
44 let protocol_type = u16::from_be_bytes([data[2], data[3]]);
45 let hardware_size = data[4];
46 let protocol_size = data[5];
47 let operation = u16::from_be_bytes([data[6], data[7]]);
48
49 fields.push(("hardware_type", FieldValue::UInt16(hardware_type)));
50 fields.push(("protocol_type", FieldValue::UInt16(protocol_type)));
51 fields.push(("hardware_size", FieldValue::UInt8(hardware_size)));
52 fields.push(("protocol_size", FieldValue::UInt8(protocol_size)));
53 fields.push(("operation", FieldValue::UInt16(operation)));
54
55 let operation_name = match operation {
57 operation::REQUEST => "Request",
58 operation::REPLY => "Reply",
59 _ => "Unknown",
60 };
61 fields.push(("operation_name", FieldValue::Str(operation_name)));
62
63 if hardware_type == 1
65 && protocol_type == ethertype::IPV4
66 && hardware_size == 6
67 && protocol_size == 4
68 {
69 fields.push(("sender_mac", FieldValue::mac(&data[8..14])));
70 fields.push(("sender_ip", FieldValue::ipv4(&data[14..18])));
71 fields.push(("target_mac", FieldValue::mac(&data[18..24])));
72 fields.push(("target_ip", FieldValue::ipv4(&data[24..28])));
73 }
74
75 ParseResult::success(fields, &data[28..], SmallVec::new())
77 }
78
79 fn schema_fields(&self) -> Vec<FieldDescriptor> {
80 vec![
81 FieldDescriptor::new("arp.hardware_type", DataKind::UInt16).set_nullable(true),
82 FieldDescriptor::new("arp.protocol_type", DataKind::UInt16).set_nullable(true),
83 FieldDescriptor::new("arp.hardware_size", DataKind::UInt8).set_nullable(true),
84 FieldDescriptor::new("arp.protocol_size", DataKind::UInt8).set_nullable(true),
85 FieldDescriptor::new("arp.operation", DataKind::UInt16).set_nullable(true),
86 FieldDescriptor::new("arp.operation_name", DataKind::String).set_nullable(true),
87 FieldDescriptor::mac_field("arp.sender_mac").set_nullable(true),
88 FieldDescriptor::new("arp.sender_ip", DataKind::String).set_nullable(true),
89 FieldDescriptor::mac_field("arp.target_mac").set_nullable(true),
90 FieldDescriptor::new("arp.target_ip", DataKind::String).set_nullable(true),
91 ]
92 }
93
94 fn dependencies(&self) -> &'static [&'static str] {
95 &["ethernet", "vlan"]
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use std::net::IpAddr;
103
104 #[test]
105 fn test_parse_arp_request() {
106 let packet = [
108 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xc0, 0xa8, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x02, ];
118
119 let parser = ArpProtocol;
120 let mut context = ParseContext::new(1);
121 context.insert_hint("ethertype", ethertype::ARP as u64);
122
123 let result = parser.parse(&packet, &context);
124
125 assert!(result.is_ok());
126 assert_eq!(result.get("operation"), Some(&FieldValue::UInt16(1)));
127 assert_eq!(
128 result.get("sender_ip"),
129 Some(&FieldValue::IpAddr(IpAddr::V4(
130 "192.168.1.1".parse().unwrap()
131 )))
132 );
133 assert_eq!(
134 result.get("operation_name"),
135 Some(&FieldValue::Str("Request"))
136 );
137 }
138
139 #[test]
140 fn test_parse_arp_reply() {
141 let packet = [
142 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x0a, 0x00, 0x00, 0x02, ];
152
153 let parser = ArpProtocol;
154 let mut context = ParseContext::new(1);
155 context.insert_hint("ethertype", ethertype::ARP as u64);
156
157 let result = parser.parse(&packet, &context);
158
159 assert!(result.is_ok());
160 assert_eq!(
161 result.get("operation"),
162 Some(&FieldValue::UInt16(operation::REPLY))
163 );
164 assert_eq!(
165 result.get("operation_name"),
166 Some(&FieldValue::Str("Reply"))
167 );
168 assert_eq!(
169 result.get("sender_ip"),
170 Some(&FieldValue::IpAddr(IpAddr::V4("10.0.0.1".parse().unwrap())))
171 );
172 assert_eq!(
173 result.get("target_ip"),
174 Some(&FieldValue::IpAddr(IpAddr::V4("10.0.0.2".parse().unwrap())))
175 );
176 }
177
178 #[test]
179 fn test_can_parse_arp() {
180 let parser = ArpProtocol;
181
182 let ctx1 = ParseContext::new(1);
184 assert!(parser.can_parse(&ctx1).is_none());
185
186 let mut ctx2 = ParseContext::new(1);
188 ctx2.insert_hint("ethertype", ethertype::IPV4 as u64);
189 assert!(parser.can_parse(&ctx2).is_none());
190
191 let mut ctx3 = ParseContext::new(1);
193 ctx3.insert_hint("ethertype", ethertype::ARP as u64);
194 assert!(parser.can_parse(&ctx3).is_some());
195 }
196
197 #[test]
198 fn test_parse_arp_too_short() {
199 let short_packet = [0x00, 0x01, 0x08, 0x00]; let parser = ArpProtocol;
202 let mut context = ParseContext::new(1);
203 context.insert_hint("ethertype", ethertype::ARP as u64);
204
205 let result = parser.parse(&short_packet, &context);
206
207 assert!(!result.is_ok());
208 assert!(result.error.is_some());
209 }
210
211 #[test]
212 fn test_arp_hardware_and_protocol_types() {
213 let packet = [
214 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xc0, 0xa8, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x02, ];
224
225 let parser = ArpProtocol;
226 let mut context = ParseContext::new(1);
227 context.insert_hint("ethertype", ethertype::ARP as u64);
228
229 let result = parser.parse(&packet, &context);
230
231 assert!(result.is_ok());
232 assert_eq!(result.get("hardware_type"), Some(&FieldValue::UInt16(1)));
233 assert_eq!(
234 result.get("protocol_type"),
235 Some(&FieldValue::UInt16(ethertype::IPV4))
236 );
237 assert_eq!(result.get("hardware_size"), Some(&FieldValue::UInt8(6)));
238 assert_eq!(result.get("protocol_size"), Some(&FieldValue::UInt8(4)));
239 }
240
241 #[test]
242 fn test_arp_no_payload() {
243 let packet = [
244 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xc0, 0xa8, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x02, ];
254
255 let parser = ArpProtocol;
256 let mut context = ParseContext::new(1);
257 context.insert_hint("ethertype", ethertype::ARP as u64);
258
259 let result = parser.parse(&packet, &context);
260
261 assert!(result.is_ok());
262 assert!(result.child_hints.is_empty());
264 assert!(result.remaining.is_empty());
265 }
266}