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)
}
|