File: duid.go

package info (click to toggle)
golang-github-mdlayher-dhcp6 0.0~git20190311.2a67805-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 400 kB
  • sloc: makefile: 3
file content (391 lines) | stat: -rw-r--r-- 11,731 bytes parent folder | download
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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
package dhcp6opts

import (
	"encoding"
	"errors"
	"io"
	"net"
	"time"

	"github.com/mdlayher/dhcp6/internal/buffer"
)

var (
	// errInvalidDUIDLLT is returned when not enough bytes are present
	// to parse a valid DUIDLLT from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDLLT = errors.New("invalid DUID-LLT")

	// errInvalidDUIDEN is returned when not enough bytes are present
	// to parse a valid DUIDEN from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDEN = errors.New("invalid DUID-EN")

	// errInvalidDUIDLL is returned when not enough bytes are present
	// to parse a valid DUIDLL from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDLL = errors.New("invalid DUID-LL")

	// errInvalidDUIDUUID is returned when not enough bytes are present
	// to parse a valid DUIDUUID from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDUUID = errors.New("invalid DUID-UUID")

	// errUnknownDUID is returned when an unknown DUID type is
	// encountered, and thus, a DUID cannot be parsed.
	errUnknownDUID = errors.New("unknown DUID type")
)

var (
	// duidLLTTime is the date specified in RFC 3315, Section 9.2, for use
	// with DUID-LLT generation.  It is used to calculate a duration from an
	// input time after this date.  Dates before this time are not valid for
	// creation of DUIDLLT values.
	duidLLTTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
)

// DUIDType is a type of DHCP Unique Identifier, as defined in RFC
// 3315, Section 9.  DUIDs are used to uniquely identify a client to a
// server, or vice-versa.
type DUIDType uint16

// DUIDType constants which indicate DUID types described in RFCs 3315 and 6355.
//
// These DUID types are taken from IANA's DHCPv6 parameters registry:
// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml.
const (
	// RFC 3315
	DUIDTypeLLT DUIDType = 1
	DUIDTypeEN  DUIDType = 2
	DUIDTypeLL  DUIDType = 3

	// RFC 6355
	DUIDTypeUUID DUIDType = 4
)

// DUID represents a DHCP Unique Identifier, as defined in RFC
// 3315, Section 9.  A DUID is used by a DHCP server to identify
// unique clients.  A DUID can also be used by a DHCP client to identify
// a unique server, when needed.
//
// The DUID interface represents a generic DUID, but DUIDs can be
// type-asserted to one of four specific types outlined in RFC 3315
// and RFC 6355:
//   -  DUIDLLT - DUID Based on Link-layer Address Plus Time
//   -   DUIDEN - DUID Assigned by Vendor Based on Enterprise Number
//   -   DUIDLL - DUID Based on Link-layer Address
//   - DUIDUUID - DUID Based on Universally Unique Identifier
//
// If further introspection of the DUID is needed, a type switch is
// recommended:
//	switch d := duid.(type) {
//	case *dhcp6.DUIDLLT:
//		fmt.Println(d.Time)
//	case *dhcp6.DUIDEN:
//		fmt.Println(d.EnterpriseNumber)
//	case *dhcp6.DUIDLL:
//		fmt.Println(d.HardwareAddr)
//	case *dhcp6.DUIDUUID:
//		fmt.Println(d.UUID)
//	}
type DUID interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler
}

// DUIDLLT represents a DUID Based on Link-layer Address Plus Time [DUID-LLT],
// as defined in RFC 3315, Section 9.2.
//
// This DUID type must only be used with clients and servers with stable,
// persistent storage.  It is the recommended DUID type for all general
// purpose computing devices.
type DUIDLLT struct {
	// Type specifies the DUID type.  For a DUIDLLT, this should always be
	// DUIDTypeLLT.
	Type DUIDType

	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// Time specifies the duration of the time this DUID was generated, minus
	// midnight (UTC), January 1, 2000.
	Time time.Duration

	// HardwareAddr specifies the hardware address for an arbitrary link-layer
	// interface on a device, used in generating the DUIDLLT.  This value
	// could represent any arbitrary interface on a system, and should not be
	// treated as a client or server's communicating hardware address.
	HardwareAddr net.HardwareAddr
}

// NewDUIDLLT generates a new DUIDLLT from an input IANA-assigned hardware
// type, time value, and a hardware address.
//
// The time value must be greater than midnight (UTC), January 1, 2000.
func NewDUIDLLT(hardwareType uint16, time time.Time, hardwareAddr net.HardwareAddr) (*DUIDLLT, error) {
	// Do not accept dates before duidLLTTime.
	if time.Before(duidLLTTime) {
		return nil, ErrInvalidDUIDLLTTime
	}

	return &DUIDLLT{
		Type:         DUIDTypeLLT,
		HardwareType: hardwareType,
		Time:         time.Sub(duidLLTTime),
		HardwareAddr: hardwareAddr,
	}, nil
}

// MarshalBinary allocates a byte slice containing the data from a DUIDLLT.
func (d *DUIDLLT) MarshalBinary() ([]byte, error) {
	// 2 bytes: DUID type
	// 2 bytes: hardware type
	// 4 bytes: time duration
	// N bytes: hardware address
	b := buffer.New(nil)

	b.Write16(uint16(d.Type))
	b.Write16(d.HardwareType)
	b.Write32(uint32(d.Time / time.Second))
	b.WriteBytes(d.HardwareAddr)

	return b.Data(), nil
}

// UnmarshalBinary unmarshals a raw byte slice into a DUIDLLT.
// If the byte slice does not contain enough data to form a valid
// DUIDLLT, or another DUID type is indicated, errInvalidDUIDLLT is returned.
func (d *DUIDLLT) UnmarshalBinary(p []byte) error {
	b := buffer.New(p)
	// Too short to be valid DUIDLLT
	if b.Len() < 8 {
		return io.ErrUnexpectedEOF
	}

	// Verify DUID type
	dType := DUIDType(b.Read16())
	if dType != DUIDTypeLLT {
		return errInvalidDUIDLLT
	}
	d.Type = dType
	d.HardwareType = b.Read16()
	d.Time = time.Duration(b.Read32()) * time.Second

	d.HardwareAddr = b.Remaining()

	return nil
}

// DUIDEN represents a DUID Assigned by Vendor Based on Enterprise Number
// [DUID-EN], as defined in RFC 3315, Section 9.3.  This DUID type
// uses an IANA-assigned Private Enterprise Number for a given vendor.
type DUIDEN struct {
	// Type specifies the DUID type.  For a DUIDEN, this should always be
	// DUIDTypeEN.
	Type DUIDType

	// EnterpriseNumber specifies an IANA-assigned vendor Private Enterprise
	// Number.
	EnterpriseNumber uint32

	// Identifier specifies a unique identifier of arbitrary length.  This
	// value is typically assigned when a device is manufactured.
	Identifier []byte
}

// NewDUIDEN generates a new DUIDEN from an input IANA-assigned Private
// Enterprise Number and a variable length unique identifier byte slice.
func NewDUIDEN(enterpriseNumber uint32, identifier []byte) *DUIDEN {
	return &DUIDEN{
		Type:             DUIDTypeEN,
		EnterpriseNumber: enterpriseNumber,
		Identifier:       identifier,
	}
}

// MarshalBinary allocates a byte slice containing the data from a DUIDEN.
func (d *DUIDEN) MarshalBinary() ([]byte, error) {
	// 2 bytes: DUID type
	// 4 bytes: enterprise number
	// N bytes: identifier
	b := buffer.New(nil)

	b.Write16(uint16(d.Type))
	b.Write32(d.EnterpriseNumber)
	b.WriteBytes(d.Identifier)

	return b.Data(), nil
}

// UnmarshalBinary unmarshals a raw byte slice into a DUIDEN.
// If the byte slice does not contain enough data to form a valid
// DUIDEN, or another DUID type is indicated, errInvalidDUIDEN is returned.
func (d *DUIDEN) UnmarshalBinary(p []byte) error {
	b := buffer.New(p)
	// Too short to be valid DUIDEN
	if b.Len() < 6 {
		return io.ErrUnexpectedEOF
	}

	// Verify DUID type
	dType := DUIDType(b.Read16())
	if dType != DUIDTypeEN {
		return errInvalidDUIDEN
	}
	d.Type = dType
	d.EnterpriseNumber = b.Read32()
	d.Identifier = b.Remaining()
	return nil
}

// DUIDLL represents a DUID Based on Link-layer Address [DUID-LL],
// as defined in RFC 3315, Section 9.4.
//
// This DUID type is recommended for devices with a
// permanently-connected network interface, but without stable,
// persistent storage.
//
// DUIDLL values are generated automatically for Servers which are not
// created with a ServerID, using the hardware type found by HardwareType
// and the hardware address of the listening network interface.
type DUIDLL struct {
	// Type specifies the DUID type.  For a DUIDLL, this should always be
	// DUIDTypeLL.
	Type DUIDType

	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// HardwareAddr specifies the hardware address for an arbitrary link-layer
	// interface on a device, used in generating the DUIDLL.  This value
	// could represent any arbitrary interface on a system, and should not be
	// treated as a client or server's communicating hardware address.
	HardwareAddr net.HardwareAddr
}

// NewDUIDLL generates a new DUIDLL from an input IANA-assigned hardware
// type and a hardware address.
func NewDUIDLL(hardwareType uint16, hardwareAddr net.HardwareAddr) *DUIDLL {
	return &DUIDLL{
		Type:         DUIDTypeLL,
		HardwareType: hardwareType,
		HardwareAddr: hardwareAddr,
	}
}

// MarshalBinary allocates a byte slice containing the data from a DUIDLL.
func (d *DUIDLL) MarshalBinary() ([]byte, error) {
	// 2 bytes: DUID type
	// 2 bytes: hardware type
	// N bytes: hardware address
	b := buffer.New(nil)

	b.Write16(uint16(d.Type))
	b.Write16(d.HardwareType)
	b.WriteBytes(d.HardwareAddr)

	return b.Data(), nil
}

// UnmarshalBinary unmarshals a raw byte slice into a DUIDLL.
// If the byte slice does not contain enough data to form a valid
// DUIDLL, or another DUID type is indicated, errInvalidDUIDLL is returned.
func (d *DUIDLL) UnmarshalBinary(p []byte) error {
	b := buffer.New(p)
	// Too short to be DUIDLL
	if b.Len() < 4 {
		return io.ErrUnexpectedEOF
	}

	// Verify DUID type
	dType := DUIDType(b.Read16())
	if dType != DUIDTypeLL {
		return errInvalidDUIDLL
	}
	d.Type = dType
	d.HardwareType = b.Read16()
	d.HardwareAddr = b.Remaining()

	return nil
}

// DUIDUUID represents a DUID based on Universally Unique Identifier
// [DUID-UUID], as defined in RFC 6355.  This DUID type uses a UUID to
// identify clients or servers.
type DUIDUUID struct {
	// Type specifies the DUID type.  For a DUIDUUID, this should always be
	// DUIDTypeUUID.
	Type DUIDType

	// UUID specifies a Universally Unique Identifier, as described in RFC 4578.
	UUID [16]byte
}

// NewDUIDUUID generates a new DUIDUUID using an input UUID.
func NewDUIDUUID(uuid [16]byte) *DUIDUUID {
	return &DUIDUUID{
		Type: DUIDTypeUUID,
		UUID: uuid,
	}
}

// MarshalBinary allocates a byte slice containing the data from a DUIDUUID.
func (d *DUIDUUID) MarshalBinary() ([]byte, error) {
	//  2 bytes: DUID type
	// 16 bytes: UUID
	b := buffer.New(nil)

	b.Write16(uint16(d.Type))
	b.WriteBytes(d.UUID[:])

	return b.Data(), nil
}

// UnmarshalBinary unmarshals a raw byte slice into a DUIDUUID.
// If the byte slice does not contain the exact number of bytes
// needed to form a valid DUIDUUID, or another DUID type is indicated,
// errInvalidDUIDUUID is returned.
func (d *DUIDUUID) UnmarshalBinary(p []byte) error {
	b := buffer.New(p)
	// DUIDUUIDs are fixed-length structures
	if b.Len() != 18 {
		return io.ErrUnexpectedEOF
	}

	// Verify DUID type
	dType := DUIDType(b.Read16())
	if dType != DUIDTypeUUID {
		return errInvalidDUIDUUID
	}
	d.Type = dType
	b.ReadBytes(d.UUID[:])
	return nil
}

// parseDUID returns the correct DUID type of the input byte slice as a
// DUID interface type.
func parseDUID(p []byte) (DUID, error) {
	b := buffer.New(p)
	// DUID must have enough bytes to determine its type
	if b.Len() < 2 {
		return nil, io.ErrUnexpectedEOF
	}

	var d DUID
	switch DUIDType(b.Read16()) {
	case DUIDTypeLLT:
		d = new(DUIDLLT)
	case DUIDTypeEN:
		d = new(DUIDEN)
	case DUIDTypeLL:
		d = new(DUIDLL)
	case DUIDTypeUUID:
		d = new(DUIDUUID)
	default:
		return nil, errUnknownDUID
	}

	return d, d.UnmarshalBinary(p)
}