blob: 052b3943bfb39d5fd109b31be80cec231fea6b70 [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001// Copyright 2018 Google, Inc. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree.
6
7package layers
8
9import (
10 "encoding/binary"
11 "errors"
12 "fmt"
13 "net"
14
15 "github.com/google/gopacket"
16)
17
18// DHCPv6MsgType represents a DHCPv6 operation
19type DHCPv6MsgType byte
20
21// Constants that represent DHCP operations
22const (
23 DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota
24 DHCPv6MsgTypeSolicit
25 DHCPv6MsgTypeAdverstise
26 DHCPv6MsgTypeRequest
27 DHCPv6MsgTypeConfirm
28 DHCPv6MsgTypeRenew
29 DHCPv6MsgTypeRebind
30 DHCPv6MsgTypeReply
31 DHCPv6MsgTypeRelease
32 DHCPv6MsgTypeDecline
33 DHCPv6MsgTypeReconfigure
34 DHCPv6MsgTypeInformationRequest
35 DHCPv6MsgTypeRelayForward
36 DHCPv6MsgTypeRelayReply
37)
38
39// String returns a string version of a DHCPv6MsgType.
40func (o DHCPv6MsgType) String() string {
41 switch o {
42 case DHCPv6MsgTypeUnspecified:
43 return "Unspecified"
44 case DHCPv6MsgTypeSolicit:
45 return "Solicit"
46 case DHCPv6MsgTypeAdverstise:
47 return "Adverstise"
48 case DHCPv6MsgTypeRequest:
49 return "Request"
50 case DHCPv6MsgTypeConfirm:
51 return "Confirm"
52 case DHCPv6MsgTypeRenew:
53 return "Renew"
54 case DHCPv6MsgTypeRebind:
55 return "Rebind"
56 case DHCPv6MsgTypeReply:
57 return "Reply"
58 case DHCPv6MsgTypeRelease:
59 return "Release"
60 case DHCPv6MsgTypeDecline:
61 return "Decline"
62 case DHCPv6MsgTypeReconfigure:
63 return "Reconfigure"
64 case DHCPv6MsgTypeInformationRequest:
65 return "InformationRequest"
66 case DHCPv6MsgTypeRelayForward:
67 return "RelayForward"
68 case DHCPv6MsgTypeRelayReply:
69 return "RelayReply"
70 default:
71 return "Unknown"
72 }
73}
74
75// DHCPv6 contains data for a single DHCP packet.
76type DHCPv6 struct {
77 BaseLayer
78 MsgType DHCPv6MsgType
79 HopCount uint8
80 LinkAddr net.IP
81 PeerAddr net.IP
82 TransactionID []byte
83 Options DHCPv6Options
84}
85
86// LayerType returns gopacket.LayerTypeDHCPv6
87func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 }
88
89// DecodeFromBytes decodes the given bytes into this layer.
90func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
91 d.BaseLayer = BaseLayer{Contents: data}
92 d.Options = d.Options[:0]
93 d.MsgType = DHCPv6MsgType(data[0])
94
95 offset := 0
96 if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
97 d.HopCount = data[1]
98 d.LinkAddr = net.IP(data[2:18])
99 d.PeerAddr = net.IP(data[18:34])
100 offset = 34
101 } else {
102 d.TransactionID = data[1:4]
103 offset = 4
104 }
105
106 stop := len(data)
107 for offset < stop {
108 o := DHCPv6Option{}
109 if err := o.decode(data[offset:]); err != nil {
110 return err
111 }
112 d.Options = append(d.Options, o)
113 offset += int(o.Length) + 4 // 2 from option code, 2 from option length
114 }
115
116 return nil
117}
118
119// Len returns the length of a DHCPv6 packet.
120func (d *DHCPv6) Len() int {
121 n := 1
122 if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
123 n += 33
124 } else {
125 n += 3
126 }
127
128 for _, o := range d.Options {
129 n += int(o.Length) + 4 // 2 from option code, 2 from option length
130 }
131
132 return n
133}
134
135// SerializeTo writes the serialized form of this layer into the
136// SerializationBuffer, implementing gopacket.SerializableLayer.
137// See the docs for gopacket.SerializableLayer for more info.
138func (d *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
139 plen := int(d.Len())
140
141 data, err := b.PrependBytes(plen)
142 if err != nil {
143 return err
144 }
145
146 offset := 0
147 data[0] = byte(d.MsgType)
148 if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
149 data[1] = byte(d.HopCount)
150 copy(data[2:18], d.LinkAddr.To16())
151 copy(data[18:34], d.PeerAddr.To16())
152 offset = 34
153 } else {
154 copy(data[1:4], d.TransactionID)
155 offset = 4
156 }
157
158 if len(d.Options) > 0 {
159 for _, o := range d.Options {
160 if err := o.encode(data[offset:], opts); err != nil {
161 return err
162 }
163 offset += int(o.Length) + 4 // 2 from option code, 2 from option length
164 }
165 }
166 return nil
167}
168
169// CanDecode returns the set of layer types that this DecodingLayer can decode.
170func (d *DHCPv6) CanDecode() gopacket.LayerClass {
171 return LayerTypeDHCPv6
172}
173
174// NextLayerType returns the layer type contained by this DecodingLayer.
175func (d *DHCPv6) NextLayerType() gopacket.LayerType {
176 return gopacket.LayerTypePayload
177}
178
179func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error {
180 dhcp := &DHCPv6{}
181 err := dhcp.DecodeFromBytes(data, p)
182 if err != nil {
183 return err
184 }
185 p.AddLayer(dhcp)
186 return p.NextDecoder(gopacket.LayerTypePayload)
187}
188
189// DHCPv6StatusCode represents a DHCP status code - RFC-3315
190type DHCPv6StatusCode uint16
191
192// Constants for the DHCPv6StatusCode.
193const (
194 DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota
195 DHCPv6StatusCodeUnspecFail
196 DHCPv6StatusCodeNoAddrsAvail
197 DHCPv6StatusCodeNoBinding
198 DHCPv6StatusCodeNotOnLink
199 DHCPv6StatusCodeUseMulticast
200)
201
202// String returns a string version of a DHCPv6StatusCode.
203func (o DHCPv6StatusCode) String() string {
204 switch o {
205 case DHCPv6StatusCodeSuccess:
206 return "Success"
207 case DHCPv6StatusCodeUnspecFail:
208 return "UnspecifiedFailure"
209 case DHCPv6StatusCodeNoAddrsAvail:
210 return "NoAddressAvailable"
211 case DHCPv6StatusCodeNoBinding:
212 return "NoBinding"
213 case DHCPv6StatusCodeNotOnLink:
214 return "NotOnLink"
215 case DHCPv6StatusCodeUseMulticast:
216 return "UseMulticast"
217 default:
218 return "Unknown"
219 }
220}
221
222// DHCPv6DUIDType represents a DHCP DUID - RFC-3315
223type DHCPv6DUIDType uint16
224
225// Constants for the DHCPv6DUIDType.
226const (
227 DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1
228 DHCPv6DUIDTypeEN
229 DHCPv6DUIDTypeLL
230)
231
232// String returns a string version of a DHCPv6DUIDType.
233func (o DHCPv6DUIDType) String() string {
234 switch o {
235 case DHCPv6DUIDTypeLLT:
236 return "LLT"
237 case DHCPv6DUIDTypeEN:
238 return "EN"
239 case DHCPv6DUIDTypeLL:
240 return "LL"
241 default:
242 return "Unknown"
243 }
244}
245
246// DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19)
247type DHCPv6DUID struct {
248 Type DHCPv6DUIDType
249 // LLT, LL
250 HardwareType []byte
251 // EN
252 EnterpriseNumber []byte
253 // LLT
254 Time []byte
255 // LLT, LL
256 LinkLayerAddress net.HardwareAddr
257 // EN
258 Identifier []byte
259}
260
261// DecodeFromBytes decodes the given bytes into a DHCPv6DUID
262func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
263 if len(data) < 2 {
264 return errors.New("Not enough bytes to decode: " + string(len(data)))
265 }
266
267 d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
268 if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
269 d.HardwareType = data[2:4]
270 }
271
272 if d.Type == DHCPv6DUIDTypeLLT {
273 d.Time = data[4:8]
274 d.LinkLayerAddress = net.HardwareAddr(data[8:])
275 } else if d.Type == DHCPv6DUIDTypeEN {
276 d.EnterpriseNumber = data[2:6]
277 d.Identifier = data[6:]
278 } else { // DHCPv6DUIDTypeLL
279 d.LinkLayerAddress = net.HardwareAddr(data[4:])
280 }
281
282 return nil
283}
284
285// Encode encodes the DHCPv6DUID in a slice of bytes
286func (d *DHCPv6DUID) Encode() []byte {
287 length := d.Len()
288 data := make([]byte, length)
289 binary.BigEndian.PutUint16(data[0:2], uint16(d.Type))
290
291 if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
292 copy(data[2:4], d.HardwareType)
293 }
294
295 if d.Type == DHCPv6DUIDTypeLLT {
296 copy(data[4:8], d.Time)
297 copy(data[8:], d.LinkLayerAddress)
298 } else if d.Type == DHCPv6DUIDTypeEN {
299 copy(data[2:6], d.EnterpriseNumber)
300 copy(data[6:], d.Identifier)
301 } else {
302 copy(data[4:], d.LinkLayerAddress)
303 }
304
305 return data
306}
307
308// Len returns the length of the DHCPv6DUID, respecting the type
309func (d *DHCPv6DUID) Len() int {
310 length := 2 // d.Type
311 if d.Type == DHCPv6DUIDTypeLLT {
312 length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress)
313 } else if d.Type == DHCPv6DUIDTypeEN {
314 length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier)
315 } else { // LL
316 length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress)
317 }
318
319 return length
320}
321
322func (d *DHCPv6DUID) String() string {
323 duid := "Type: " + d.Type.String() + ", "
324 if d.Type == DHCPv6DUIDTypeLLT {
325 duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress)
326 } else if d.Type == DHCPv6DUIDTypeEN {
327 duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier)
328 } else { // DHCPv6DUIDTypeLL
329 duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress)
330 }
331 return duid
332}
333
334func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) {
335 duid := &DHCPv6DUID{}
336 err := duid.DecodeFromBytes(data)
337 if err != nil {
338 return nil, err
339 }
340 return duid, nil
341}