1 package arp
2
3 import (
4 "bytes"
5 "io"
6 "net"
7 "net/netip"
8 "reflect"
9 "testing"
10
11 "github.com/mdlayher/ethernet"
12 )
13
14 func TestNewPacket(t *testing.T) {
15 zeroHW := net.HardwareAddr{0, 0, 0, 0, 0, 0}
16
17 iboip1 := net.HardwareAddr(bytes.Repeat([]byte{0}, 20))
18
19 tests := []struct {
20 desc string
21 op Operation
22 srcHW net.HardwareAddr
23 srcIP netip.Addr
24 dstHW net.HardwareAddr
25 dstIP netip.Addr
26 p *Packet
27 err error
28 }{
29 {
30 desc: "short source hardware address",
31 srcHW: net.HardwareAddr{0, 0, 0, 0, 0},
32 err: ErrInvalidHardwareAddr,
33 },
34 {
35 desc: "short destination hardware address",
36 srcHW: zeroHW,
37 dstHW: net.HardwareAddr{0, 0, 0, 0, 0},
38 err: ErrInvalidHardwareAddr,
39 },
40 {
41 desc: "hardware address length mismatch",
42 srcHW: zeroHW,
43 dstHW: net.HardwareAddr{0, 0, 0, 0, 0, 0, 0, 0},
44 err: ErrInvalidHardwareAddr,
45 },
46 {
47 desc: "IPv6 source IP address",
48 srcHW: zeroHW,
49 dstHW: zeroHW,
50 srcIP: netip.IPv6Unspecified(),
51 err: ErrInvalidIP,
52 },
53 {
54 desc: "IPv6 destination IP address",
55 srcHW: zeroHW,
56 dstHW: zeroHW,
57 srcIP: netip.IPv4Unspecified(),
58 dstIP: netip.IPv6Unspecified(),
59 err: ErrInvalidIP,
60 },
61 {
62 desc: "Gratuitous ARP request, IPoIB hardware addresses",
63 op: OperationRequest,
64 srcHW: iboip1,
65 dstHW: ethernet.Broadcast,
66 srcIP: netip.IPv4Unspecified(),
67 dstIP: netip.IPv4Unspecified(),
68 p: &Packet{
69 HardwareType: 1,
70 ProtocolType: uint16(ethernet.EtherTypeIPv4),
71 HardwareAddrLength: 20,
72 IPLength: 4,
73 Operation: OperationRequest,
74 SenderHardwareAddr: iboip1,
75 SenderIP: netip.IPv4Unspecified(),
76 TargetHardwareAddr: ethernet.Broadcast,
77 TargetIP: netip.IPv4Unspecified(),
78 },
79 },
80 {
81 desc: "OK",
82 op: OperationRequest,
83 srcHW: zeroHW,
84 dstHW: zeroHW,
85 srcIP: netip.IPv4Unspecified(),
86 dstIP: netip.IPv4Unspecified(),
87 p: &Packet{
88 HardwareType: 1,
89 ProtocolType: uint16(ethernet.EtherTypeIPv4),
90 HardwareAddrLength: 6,
91 IPLength: 4,
92 Operation: OperationRequest,
93 SenderHardwareAddr: zeroHW,
94 SenderIP: netip.IPv4Unspecified(),
95 TargetHardwareAddr: zeroHW,
96 TargetIP: netip.IPv4Unspecified(),
97 },
98 },
99 }
100
101 for i, tt := range tests {
102 p, err := NewPacket(tt.op, tt.srcHW, tt.srcIP, tt.dstHW, tt.dstIP)
103 if err != nil {
104 if want, got := tt.err, err; want != got {
105 t.Fatalf("[%02d] test %q, unexpected error: %v != %v",
106 i, tt.desc, want, got)
107 }
108
109 continue
110 }
111
112 if want, got := tt.p, p; !reflect.DeepEqual(want, got) {
113 t.Fatalf("[%02d] test %q, unexpected Packet:\n- want: %v\n- got: %v",
114 i, tt.desc, want, got)
115 }
116 }
117 }
118
119 func TestPacketMarshalBinary(t *testing.T) {
120 zeroHW := net.HardwareAddr{0, 0, 0, 0, 0, 0}
121 ip1 := netip.MustParseAddr("192.168.1.10")
122 ip2 := netip.MustParseAddr("192.168.1.1")
123
124 iboip1 := net.HardwareAddr(bytes.Repeat([]byte{0}, 20))
125 iboip2 := net.HardwareAddr(bytes.Repeat([]byte{1}, 20))
126
127 tests := []struct {
128 desc string
129 p *Packet
130 b []byte
131 }{
132 {
133 desc: "ARP request to ethernet broadcast, 6 byte hardware addresses",
134 p: &Packet{
135 HardwareType: 1,
136 ProtocolType: uint16(ethernet.EtherTypeIPv4),
137 HardwareAddrLength: 6,
138 IPLength: 4,
139 Operation: OperationRequest,
140 SenderHardwareAddr: zeroHW,
141 SenderIP: ip1,
142 TargetHardwareAddr: ethernet.Broadcast,
143 TargetIP: ip2,
144 },
145 b: []byte{
146 0, 1,
147 8, 0,
148 6,
149 4,
150 0, 1,
151 0, 0, 0, 0, 0, 0,
152 192, 168, 1, 10,
153 255, 255, 255, 255, 255, 255,
154 192, 168, 1, 1,
155 },
156 },
157 {
158 desc: "ARP reply over infiniband, 20 byte hardware addresses",
159 p: &Packet{
160 HardwareType: 32,
161 ProtocolType: uint16(ethernet.EtherTypeIPv4),
162 HardwareAddrLength: 20,
163 IPLength: 4,
164 Operation: OperationReply,
165 SenderHardwareAddr: iboip1,
166 SenderIP: ip1,
167 TargetHardwareAddr: iboip2,
168 TargetIP: ip2,
169 },
170 b: []byte{
171 0, 32,
172 8, 0,
173 20,
174 4,
175 0, 2,
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
178 192, 168, 1, 10,
179 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
181 192, 168, 1, 1,
182 },
183 },
184 }
185
186 for i, tt := range tests {
187 b, err := tt.p.MarshalBinary()
188 if err != nil {
189 t.Fatal(err)
190 }
191
192 if want, got := tt.b, b; !bytes.Equal(want, got) {
193 t.Fatalf("[%02d] test %q, unexpected Packet bytes:\n- want: %v\n- got: %v",
194 i, tt.desc, want, got)
195 }
196 }
197 }
198
199 func TestPacketUnmarshalBinary(t *testing.T) {
200 zeroHW := net.HardwareAddr{0, 0, 0, 0, 0, 0}
201 ip1 := netip.MustParseAddr("192.168.1.10")
202 ip2 := netip.MustParseAddr("192.168.1.1")
203
204 iboip1 := net.HardwareAddr(bytes.Repeat([]byte{0}, 20))
205 iboip2 := net.HardwareAddr(bytes.Repeat([]byte{1}, 20))
206
207 tests := []struct {
208 desc string
209 p *Packet
210 b []byte
211 err error
212 }{
213 {
214 desc: "short buffer",
215 b: bytes.Repeat([]byte{0}, 7),
216 err: io.ErrUnexpectedEOF,
217 },
218 {
219 desc: "short buffer, too short for hardware addresses",
220 b: []byte{
221 0, 1,
222 8, 0,
223 255,
224 4,
225 0, 1,
226 },
227 err: io.ErrUnexpectedEOF,
228 },
229 {
230 desc: "short buffer, too short for IP addresses",
231 b: []byte{
232 0, 1,
233 8, 0,
234 6,
235 255,
236 0, 1,
237 },
238 err: io.ErrUnexpectedEOF,
239 },
240 {
241 desc: "ARP request to ethernet broadcast, 6 byte hardware addresses",
242 b: []byte{
243 0, 1,
244 8, 0,
245 6,
246 4,
247 0, 1,
248 0, 0, 0, 0, 0, 0,
249 192, 168, 1, 10,
250 255, 255, 255, 255, 255, 255,
251 192, 168, 1, 1,
252 },
253 p: &Packet{
254 HardwareType: 1,
255 ProtocolType: uint16(ethernet.EtherTypeIPv4),
256 HardwareAddrLength: 6,
257 IPLength: 4,
258 Operation: OperationRequest,
259 SenderHardwareAddr: zeroHW,
260 SenderIP: ip1,
261 TargetHardwareAddr: ethernet.Broadcast,
262 TargetIP: ip2,
263 },
264 },
265 {
266 desc: "ARP reply over infiniband, 20 byte hardware addresses",
267 b: []byte{
268 0, 32,
269 8, 0,
270 20,
271 4,
272 0, 2,
273 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
274 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
275 192, 168, 1, 10,
276 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
277 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
278 192, 168, 1, 1,
279 },
280 p: &Packet{
281 HardwareType: 32,
282 ProtocolType: uint16(ethernet.EtherTypeIPv4),
283 HardwareAddrLength: 20,
284 IPLength: 4,
285 Operation: OperationReply,
286 SenderHardwareAddr: iboip1,
287 SenderIP: ip1,
288 TargetHardwareAddr: iboip2,
289 TargetIP: ip2,
290 },
291 },
292 }
293
294 for i, tt := range tests {
295 p := new(Packet)
296 if err := p.UnmarshalBinary(tt.b); err != nil {
297 if want, got := tt.err, err; want != got {
298 t.Fatalf("[%02d] test %q, unexpected error: %v != %v",
299 i, tt.desc, want, got)
300 }
301
302 continue
303 }
304
305 if want, got := tt.p, p; !reflect.DeepEqual(want, got) {
306 t.Fatalf("[%02d] test %q, unexpected Packet bytes:\n- want: %v\n- got: %v",
307 i, tt.desc, want, got)
308 }
309 }
310 }
311
312 func Test_parsePacket(t *testing.T) {
313 tests := []struct {
314 desc string
315 buf []byte
316 p *Packet
317 err error
318 }{
319 {
320 desc: "invalid ethernet frame",
321 err: io.ErrUnexpectedEOF,
322 },
323 {
324 desc: "non-ARP EtherType",
325
326
327 buf: make([]byte, 56),
328 err: errInvalidARPPacket,
329 },
330 {
331 desc: "invalid ARP packet",
332 buf: append([]byte{
333
334 0, 0, 0, 0, 0, 0,
335 0, 0, 0, 0, 0, 0,
336 0x08, 0x06,
337
338 0, 0,
339 0, 0,
340 255, 255,
341 }, make([]byte, 40)...),
342 err: io.ErrUnexpectedEOF,
343 },
344 {
345 desc: "OK",
346 buf: append([]byte{
347
348 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
349 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
350 0x08, 0x06,
351
352 0, 1,
353 0x08, 0x06,
354 6,
355 4,
356 0, 2,
357 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
358 192, 168, 1, 10,
359 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
360 192, 168, 1, 1,
361 }, make([]byte, 40)...),
362 p: &Packet{
363 HardwareType: 1,
364 ProtocolType: 2054,
365 HardwareAddrLength: 6,
366 IPLength: 4,
367 Operation: OperationReply,
368 SenderHardwareAddr: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
369 SenderIP: netip.MustParseAddr("192.168.1.10"),
370 TargetHardwareAddr: net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad},
371 TargetIP: netip.MustParseAddr("192.168.1.1"),
372 },
373 },
374 }
375
376 for i, tt := range tests {
377 p, _, err := parsePacket(tt.buf)
378 if err != nil {
379 if want, got := tt.err, err; want != got {
380 t.Fatalf("[%02d] test %q, unexpected error: %v != %v",
381 i, tt.desc, want, got)
382 }
383
384 continue
385 }
386
387 if want, got := tt.p, p; !reflect.DeepEqual(want, got) {
388 t.Fatalf("[%02d] test %q, unexpected Packet:\n- want: %v\n- got: %v",
389 i, tt.desc, want, got)
390 }
391 }
392 }
393
394
395
396 func BenchmarkPacketMarshalBinary(b *testing.B) {
397 p, err := NewPacket(
398 OperationRequest,
399 net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde},
400 netip.MustParseAddr("192.168.1.10"),
401 net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad},
402 netip.MustParseAddr("192.168.1.1"),
403 )
404 if err != nil {
405 b.Fatal(err)
406 }
407
408 benchmarkPacketMarshalBinary(b, p)
409 }
410
411 func benchmarkPacketMarshalBinary(b *testing.B, p *Packet) {
412 b.ResetTimer()
413 b.ReportAllocs()
414 for i := 0; i < b.N; i++ {
415 if _, err := p.MarshalBinary(); err != nil {
416 b.Fatal(err)
417 }
418 }
419 }
420
421
422
423 func BenchmarkPacketUnmarshalBinary(b *testing.B) {
424 p, err := NewPacket(
425 OperationRequest,
426 net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde},
427 netip.MustParseAddr("192.168.1.10"),
428 net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad},
429 netip.MustParseAddr("192.168.1.1"),
430 )
431 if err != nil {
432 b.Fatal(err)
433 }
434
435 benchmarkPacketUnmarshalBinary(b, p)
436 }
437
438 func benchmarkPacketUnmarshalBinary(b *testing.B, p *Packet) {
439 pb, err := p.MarshalBinary()
440 if err != nil {
441 b.Fatal(err)
442 }
443
444 b.ResetTimer()
445 b.ReportAllocs()
446 for i := 0; i < b.N; i++ {
447 if err := p.UnmarshalBinary(pb); err != nil {
448 b.Fatal(err)
449 }
450 }
451 }
452
View as plain text