1use alloc::vec;
4use alloc::vec::Vec;
5
6use crate::types::{ArpHardwareType, ArpOperation, EtherType};
7use crate::util::{read_u16be, write_u16be, BuildError};
8
9pub const ARP_HEADER_LEN: usize = 28;
11
12#[derive(Debug, Clone)]
14pub struct ArpPacket<'a> {
15 buf: &'a [u8],
16}
17
18impl<'a> ArpPacket<'a> {
19 pub fn new(buf: &'a [u8]) -> Option<Self> {
22 if buf.len() < ARP_HEADER_LEN {
23 return None;
24 }
25 Some(Self { buf })
26 }
27
28 #[inline]
29 pub fn hardware_type(&self) -> ArpHardwareType {
30 ArpHardwareType::from(read_u16be(&self.buf[..2]))
31 }
32
33 #[inline]
34 pub fn protocol_type(&self) -> EtherType {
35 EtherType::from(read_u16be(&self.buf[2..4]))
36 }
37
38 #[inline]
39 pub fn hw_addr_len(&self) -> u8 {
40 self.buf[4]
41 }
42
43 #[inline]
44 pub fn proto_addr_len(&self) -> u8 {
45 self.buf[5]
46 }
47
48 #[inline]
49 pub fn operation(&self) -> ArpOperation {
50 ArpOperation::from(read_u16be(&self.buf[6..8]))
51 }
52
53 #[inline]
54 pub fn sender_hw_addr(&self) -> &'a [u8] {
55 let len = self.hw_addr_len() as usize;
56 &self.buf[8..8 + len]
57 }
58
59 #[inline]
60 pub fn sender_proto_addr(&self) -> &'a [u8] {
61 let hw_len = self.hw_addr_len() as usize;
62 let start = 8 + hw_len;
63 let len = self.proto_addr_len() as usize;
64 &self.buf[start..start + len]
65 }
66
67 #[inline]
68 pub fn target_hw_addr(&self) -> &'a [u8] {
69 let hw_len = self.hw_addr_len() as usize;
70 let proto_len = self.proto_addr_len() as usize;
71 let start = 8 + hw_len + proto_len;
72 &self.buf[start..start + hw_len]
73 }
74
75 #[inline]
76 pub fn target_proto_addr(&self) -> &'a [u8] {
77 let hw_len = self.hw_addr_len() as usize;
78 let proto_len = self.proto_addr_len() as usize;
79 let start = 8 + hw_len + proto_len + hw_len;
80 &self.buf[start..start + proto_len]
81 }
82
83 #[inline]
84 pub fn is_request(&self) -> bool {
85 self.operation() == ArpOperation::Request
86 }
87
88 #[inline]
89 pub fn is_reply(&self) -> bool {
90 self.operation() == ArpOperation::Reply
91 }
92}
93
94#[derive(Debug, Clone)]
100pub struct ArpPacketBuilder {
101 hw_type: u16,
102 proto_type: u16,
103 hw_addr_len: u8,
104 proto_addr_len: u8,
105 operation: u16,
106 sender_hw: Vec<u8>,
107 sender_proto: Vec<u8>,
108 target_hw: Vec<u8>,
109 target_proto: Vec<u8>,
110}
111
112impl Default for ArpPacketBuilder {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118impl ArpPacketBuilder {
119 pub fn new() -> Self {
121 Self {
122 hw_type: 1, proto_type: 0x0800, hw_addr_len: 6,
125 proto_addr_len: 4,
126 operation: 1, sender_hw: vec![0u8; 6],
128 sender_proto: vec![0u8; 4],
129 target_hw: vec![0u8; 6],
130 target_proto: vec![0u8; 4],
131 }
132 }
133
134 pub fn hardware_type(mut self, ht: ArpHardwareType) -> Self {
135 self.hw_type = ht.into();
136 self
137 }
138
139 pub fn protocol_type(mut self, pt: EtherType) -> Self {
140 self.proto_type = pt.into();
141 self
142 }
143
144 pub fn operation(mut self, op: ArpOperation) -> Self {
145 self.operation = op.into();
146 self
147 }
148
149 pub fn sender_hw_addr(mut self, addr: &[u8]) -> Result<Self, BuildError> {
150 if addr.len() as u8 != self.hw_addr_len {
151 return Err(BuildError::InvalidField {
152 field: "sender_hw_addr",
153 reason: "length mismatch",
154 });
155 }
156 self.sender_hw = addr.to_vec();
157 Ok(self)
158 }
159
160 pub fn sender_proto_addr(mut self, addr: &[u8]) -> Result<Self, BuildError> {
161 if addr.len() as u8 != self.proto_addr_len {
162 return Err(BuildError::InvalidField {
163 field: "sender_proto_addr",
164 reason: "length mismatch",
165 });
166 }
167 self.sender_proto = addr.to_vec();
168 Ok(self)
169 }
170
171 pub fn target_hw_addr(mut self, addr: &[u8]) -> Result<Self, BuildError> {
172 if addr.len() as u8 != self.hw_addr_len {
173 return Err(BuildError::InvalidField {
174 field: "target_hw_addr",
175 reason: "length mismatch",
176 });
177 }
178 self.target_hw = addr.to_vec();
179 Ok(self)
180 }
181
182 pub fn target_proto_addr(mut self, addr: &[u8]) -> Result<Self, BuildError> {
183 if addr.len() as u8 != self.proto_addr_len {
184 return Err(BuildError::InvalidField {
185 field: "target_proto_addr",
186 reason: "length mismatch",
187 });
188 }
189 self.target_proto = addr.to_vec();
190 Ok(self)
191 }
192
193 pub fn build(self) -> Vec<u8> {
194 let total = 8 + self.sender_hw.len() + self.sender_proto.len()
195 + self.target_hw.len() + self.target_proto.len();
196 let mut buf = Vec::with_capacity(total);
197
198 write_u16be(&mut [0u8; 2], self.hw_type);
199 buf.extend_from_slice(&self.hw_type.to_be_bytes());
200 buf.extend_from_slice(&self.proto_type.to_be_bytes());
201 buf.push(self.hw_addr_len);
202 buf.push(self.proto_addr_len);
203 buf.extend_from_slice(&self.operation.to_be_bytes());
204 buf.extend_from_slice(&self.sender_hw);
205 buf.extend_from_slice(&self.sender_proto);
206 buf.extend_from_slice(&self.target_hw);
207 buf.extend_from_slice(&self.target_proto);
208
209 buf
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 const SAMPLE_ARP_REQUEST: &[u8] = &[
219 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA8, 0x01, 0x02, ];
229
230 #[test]
231 fn parse_arp_request() {
232 let pkt = ArpPacket::new(SAMPLE_ARP_REQUEST).unwrap();
233 assert_eq!(pkt.hardware_type(), ArpHardwareType::Ethernet);
234 assert_eq!(pkt.protocol_type(), EtherType::Ipv4);
235 assert!(pkt.is_request());
236 assert!(!pkt.is_reply());
237 assert_eq!(pkt.sender_hw_addr(), &[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
238 assert_eq!(pkt.sender_proto_addr(), &[192, 168, 1, 1]);
239 assert_eq!(pkt.target_hw_addr(), &[0u8; 6]);
240 assert_eq!(pkt.target_proto_addr(), &[192, 168, 1, 2]);
241 }
242
243 #[test]
244 fn parse_too_short() {
245 assert!(ArpPacket::new(&[0u8; 27]).is_none());
246 assert!(ArpPacket::new(&[]).is_none());
247 }
248
249 #[test]
250 fn build_and_parse_roundtrip() {
251 let buf = ArpPacketBuilder::new()
252 .operation(ArpOperation::Reply)
253 .sender_hw_addr(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])
254 .unwrap()
255 .sender_proto_addr(&[10, 0, 0, 1])
256 .unwrap()
257 .target_hw_addr(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF])
258 .unwrap()
259 .target_proto_addr(&[10, 0, 0, 2])
260 .unwrap()
261 .build();
262
263 let pkt = ArpPacket::new(&buf).unwrap();
264 assert!(pkt.is_reply());
265 assert_eq!(pkt.sender_hw_addr(), &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66]);
266 assert_eq!(pkt.target_proto_addr(), &[10, 0, 0, 2]);
267 }
268}