pcapsql_core/protocol/
arp.rs

1//! ARP protocol parser.
2
3use smallvec::SmallVec;
4
5use super::ethernet::ethertype;
6use super::{FieldValue, ParseContext, ParseResult, Protocol};
7use crate::schema::{DataKind, FieldDescriptor};
8
9/// ARP operation codes.
10pub mod operation {
11    pub const REQUEST: u16 = 1;
12    pub const REPLY: u16 = 2;
13}
14
15/// ARP protocol parser.
16#[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        // ARP for Ethernet/IPv4 is 28 bytes
37        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        // Operation name for convenience (zero-copy static string)
56        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        // For Ethernet/IPv4 ARP (most common case)
64        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        // ARP doesn't have payload protocols
76        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        // ARP Request for 192.168.1.2 from 192.168.1.1
107        let packet = [
108            0x00, 0x01, // Hardware type: Ethernet
109            0x08, 0x00, // Protocol type: IPv4
110            0x06, // Hardware size: 6
111            0x04, // Protocol size: 4
112            0x00, 0x01, // Operation: Request
113            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // Sender MAC
114            0xc0, 0xa8, 0x01, 0x01, // Sender IP: 192.168.1.1
115            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Target MAC (unknown)
116            0xc0, 0xa8, 0x01, 0x02, // Target IP: 192.168.1.2
117        ];
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, // Hardware type: Ethernet
143            0x08, 0x00, // Protocol type: IPv4
144            0x06, // Hardware size: 6
145            0x04, // Protocol size: 4
146            0x00, 0x02, // Operation: Reply
147            0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // Sender MAC
148            0x0a, 0x00, 0x00, 0x01, // Sender IP: 10.0.0.1
149            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // Target MAC
150            0x0a, 0x00, 0x00, 0x02, // Target IP: 10.0.0.2
151        ];
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        // Without hint
183        let ctx1 = ParseContext::new(1);
184        assert!(parser.can_parse(&ctx1).is_none());
185
186        // With IPv4 ethertype
187        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        // With ARP ethertype
192        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]; // Only 4 bytes
200
201        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, // Hardware type: Ethernet (1)
215            0x08, 0x00, // Protocol type: IPv4 (0x0800)
216            0x06, // Hardware size: 6
217            0x04, // Protocol size: 4
218            0x00, 0x01, // Operation: Request
219            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // Sender MAC
220            0xc0, 0xa8, 0x01, 0x01, // Sender IP
221            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Target MAC
222            0xc0, 0xa8, 0x01, 0x02, // Target IP
223        ];
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, // Hardware type: Ethernet
245            0x08, 0x00, // Protocol type: IPv4
246            0x06, // Hardware size: 6
247            0x04, // Protocol size: 4
248            0x00, 0x01, // Operation: Request
249            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // Sender MAC
250            0xc0, 0xa8, 0x01, 0x01, // Sender IP
251            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Target MAC
252            0xc0, 0xa8, 0x01, 0x02, // Target IP
253        ];
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        // ARP doesn't have child protocols
263        assert!(result.child_hints.is_empty());
264        assert!(result.remaining.is_empty());
265    }
266}