forked from mdlayher/arp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
packet.go
250 lines (199 loc) · 6.58 KB
/
packet.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package arp
import (
"encoding/binary"
"errors"
"io"
"net"
"github.com/mdlayher/ethernet"
)
var (
// ErrInvalidHardwareAddr is returned when one or more invalid hardware
// addresses are passed to NewPacket.
ErrInvalidHardwareAddr = errors.New("invalid hardware address")
// ErrInvalidIP is returned when one or more invalid IPv4 addresses are
// passed to NewPacket.
ErrInvalidIP = errors.New("invalid IPv4 address")
// errInvalidARPPacket is returned when an ethernet frame does not
// indicate that an ARP packet is contained in its payload.
errInvalidARPPacket = errors.New("invalid ARP packet")
)
//go:generate stringer -output=string.go -type=Operation
// An Operation is an ARP operation, such as request or reply.
type Operation uint16
// Operation constants which indicate an ARP request or reply.
const (
OperationRequest Operation = 1
OperationReply Operation = 2
)
// A Packet is a raw ARP packet, as described in RFC 826.
type Packet struct {
// HardwareType specifies an IANA-assigned hardware type, as described
// in RFC 826.
HardwareType uint16
// ProtocolType specifies the internetwork protocol for which the ARP
// request is intended. Typically, this is the IPv4 EtherType.
ProtocolType uint16
// HardwareAddrLength specifies the length of the sender and target
// hardware addresses included in a Packet.
HardwareAddrLength uint8
// IPLength specifies the length of the sender and target IPv4 addresses
// included in a Packet.
IPLength uint8
// Operation specifies the ARP operation being performed, such as request
// or reply.
Operation Operation
// SenderHardwareAddr specifies the hardware address of the sender of this
// Packet.
SenderHardwareAddr net.HardwareAddr
// SenderIP specifies the IPv4 address of the sender of this Packet.
SenderIP net.IP
// TargetHardwareAddr specifies the hardware address of the target of this
// Packet.
TargetHardwareAddr net.HardwareAddr
// TargetIP specifies the IPv4 address of the target of this Packet.
TargetIP net.IP
}
// NewPacket creates a new Packet from an input Operation and hardware/IPv4
// address values for both a sender and target.
//
// If either hardware address is less than 6 bytes in length, or there is a
// length mismatch between the two, ErrInvalidHardwareAddr is returned.
//
// If either IP address is not an IPv4 address, or there is a length mismatch
// between the two, ErrInvalidIP is returned.
func NewPacket(op Operation, srcHW net.HardwareAddr, srcIP net.IP, dstHW net.HardwareAddr, dstIP net.IP) (*Packet, error) {
// Validate hardware addresses for minimum length, and matching length
if len(srcHW) < 6 {
return nil, ErrInvalidHardwareAddr
}
if len(dstHW) < 6 {
return nil, ErrInvalidHardwareAddr
}
if len(srcHW) != len(dstHW) {
return nil, ErrInvalidHardwareAddr
}
// Validate IP addresses to ensure they are IPv4 addresses, and
// correct length
srcIP = srcIP.To4()
if srcIP == nil {
return nil, ErrInvalidIP
}
dstIP = dstIP.To4()
if dstIP == nil {
return nil, ErrInvalidIP
}
return &Packet{
// There is no Go-native way to detect hardware type of a network
// interface, so default to 1 (ethernet 10Mb) for now
HardwareType: 1,
// Default to EtherType for IPv4
ProtocolType: uint16(ethernet.EtherTypeIPv4),
// Populate other fields using input data
HardwareAddrLength: uint8(len(srcHW)),
IPLength: uint8(len(srcIP)),
Operation: op,
SenderHardwareAddr: srcHW,
SenderIP: srcIP,
TargetHardwareAddr: dstHW,
TargetIP: dstIP,
}, nil
}
// MarshalBinary allocates a byte slice containing the data from a Packet.
//
// MarshalBinary never returns an error.
func (p *Packet) MarshalBinary() ([]byte, error) {
// 2 bytes: hardware type
// 2 bytes: protocol type
// 1 byte : hardware address length
// 1 byte : protocol length
// 2 bytes: operation
// N bytes: source hardware address
// N bytes: source protocol address
// N bytes: target hardware address
// N bytes: target protocol address
// Though an IPv4 address should always 4 bytes, go-fuzz
// very quickly created several crasher scenarios which
// indicated that these values can lie.
b := make([]byte, 2+2+1+1+2+(p.IPLength*2)+(p.HardwareAddrLength*2))
// Marshal fixed length data
binary.BigEndian.PutUint16(b[0:2], p.HardwareType)
binary.BigEndian.PutUint16(b[2:4], p.ProtocolType)
b[4] = p.HardwareAddrLength
b[5] = p.IPLength
binary.BigEndian.PutUint16(b[6:8], uint16(p.Operation))
// Marshal variable length data at correct offset using lengths
// defined in p
n := 8
hal := int(p.HardwareAddrLength)
pl := int(p.IPLength)
copy(b[n:n+hal], p.SenderHardwareAddr)
n += hal
copy(b[n:n+pl], p.SenderIP)
n += pl
copy(b[n:n+hal], p.TargetHardwareAddr)
n += hal
copy(b[n:n+pl], p.TargetIP)
return b, nil
}
// UnmarshalBinary unmarshals a raw byte slice into a Packet.
func (p *Packet) UnmarshalBinary(b []byte) error {
// Must have enough room to retrieve hardware address and IP lengths
if len(b) < 8 {
return io.ErrUnexpectedEOF
}
// Retrieve fixed length data
p.HardwareType = binary.BigEndian.Uint16(b[0:2])
p.ProtocolType = binary.BigEndian.Uint16(b[2:4])
p.HardwareAddrLength = b[4]
p.IPLength = b[5]
p.Operation = Operation(binary.BigEndian.Uint16(b[6:8]))
// Unmarshal variable length data at correct offset using lengths
// defined by ml and il
//
// These variables are meant to improve readability of offset calculations
// for the code below
n := 8
ml := int(p.HardwareAddrLength)
ml2 := ml * 2
il := int(p.IPLength)
il2 := il * 2
// Must have enough room to retrieve both hardware address and IP addresses
addrl := n + ml2 + il2
if len(b) < addrl {
return io.ErrUnexpectedEOF
}
// Allocate single byte slice to store address information, which
// is resliced into fields
bb := make([]byte, addrl-n)
// Sender hardware address
copy(bb[0:ml], b[n:n+ml])
p.SenderHardwareAddr = bb[0:ml]
n += ml
// Sender IP address
copy(bb[ml:ml+il], b[n:n+il])
p.SenderIP = bb[ml : ml+il]
n += il
// Target hardware address
copy(bb[ml+il:ml2+il], b[n:n+ml])
p.TargetHardwareAddr = bb[ml+il : ml2+il]
n += ml
// Target IP address
copy(bb[ml2+il:ml2+il2], b[n:n+il])
p.TargetIP = bb[ml2+il : ml2+il2]
return nil
}
func parsePacket(buf []byte) (*Packet, *ethernet.Frame, error) {
f := new(ethernet.Frame)
if err := f.UnmarshalBinary(buf); err != nil {
return nil, nil, err
}
// Ignore frames which do not have ARP EtherType
if f.EtherType != ethernet.EtherTypeARP {
return nil, nil, errInvalidARPPacket
}
p := new(Packet)
if err := p.UnmarshalBinary(f.Payload); err != nil {
return nil, nil, err
}
return p, f, nil
}