1// 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}
342