blob: 052b3943bfb39d5fd109b31be80cec231fea6b70 [file] [log] [blame]
// Copyright 2018 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
"github.com/google/gopacket"
)
// DHCPv6MsgType represents a DHCPv6 operation
type DHCPv6MsgType byte
// Constants that represent DHCP operations
const (
DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota
DHCPv6MsgTypeSolicit
DHCPv6MsgTypeAdverstise
DHCPv6MsgTypeRequest
DHCPv6MsgTypeConfirm
DHCPv6MsgTypeRenew
DHCPv6MsgTypeRebind
DHCPv6MsgTypeReply
DHCPv6MsgTypeRelease
DHCPv6MsgTypeDecline
DHCPv6MsgTypeReconfigure
DHCPv6MsgTypeInformationRequest
DHCPv6MsgTypeRelayForward
DHCPv6MsgTypeRelayReply
)
// String returns a string version of a DHCPv6MsgType.
func (o DHCPv6MsgType) String() string {
switch o {
case DHCPv6MsgTypeUnspecified:
return "Unspecified"
case DHCPv6MsgTypeSolicit:
return "Solicit"
case DHCPv6MsgTypeAdverstise:
return "Adverstise"
case DHCPv6MsgTypeRequest:
return "Request"
case DHCPv6MsgTypeConfirm:
return "Confirm"
case DHCPv6MsgTypeRenew:
return "Renew"
case DHCPv6MsgTypeRebind:
return "Rebind"
case DHCPv6MsgTypeReply:
return "Reply"
case DHCPv6MsgTypeRelease:
return "Release"
case DHCPv6MsgTypeDecline:
return "Decline"
case DHCPv6MsgTypeReconfigure:
return "Reconfigure"
case DHCPv6MsgTypeInformationRequest:
return "InformationRequest"
case DHCPv6MsgTypeRelayForward:
return "RelayForward"
case DHCPv6MsgTypeRelayReply:
return "RelayReply"
default:
return "Unknown"
}
}
// DHCPv6 contains data for a single DHCP packet.
type DHCPv6 struct {
BaseLayer
MsgType DHCPv6MsgType
HopCount uint8
LinkAddr net.IP
PeerAddr net.IP
TransactionID []byte
Options DHCPv6Options
}
// LayerType returns gopacket.LayerTypeDHCPv6
func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 }
// DecodeFromBytes decodes the given bytes into this layer.
func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
d.BaseLayer = BaseLayer{Contents: data}
d.Options = d.Options[:0]
d.MsgType = DHCPv6MsgType(data[0])
offset := 0
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
d.HopCount = data[1]
d.LinkAddr = net.IP(data[2:18])
d.PeerAddr = net.IP(data[18:34])
offset = 34
} else {
d.TransactionID = data[1:4]
offset = 4
}
stop := len(data)
for offset < stop {
o := DHCPv6Option{}
if err := o.decode(data[offset:]); err != nil {
return err
}
d.Options = append(d.Options, o)
offset += int(o.Length) + 4 // 2 from option code, 2 from option length
}
return nil
}
// Len returns the length of a DHCPv6 packet.
func (d *DHCPv6) Len() int {
n := 1
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
n += 33
} else {
n += 3
}
for _, o := range d.Options {
n += int(o.Length) + 4 // 2 from option code, 2 from option length
}
return n
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (d *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
plen := int(d.Len())
data, err := b.PrependBytes(plen)
if err != nil {
return err
}
offset := 0
data[0] = byte(d.MsgType)
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
data[1] = byte(d.HopCount)
copy(data[2:18], d.LinkAddr.To16())
copy(data[18:34], d.PeerAddr.To16())
offset = 34
} else {
copy(data[1:4], d.TransactionID)
offset = 4
}
if len(d.Options) > 0 {
for _, o := range d.Options {
if err := o.encode(data[offset:], opts); err != nil {
return err
}
offset += int(o.Length) + 4 // 2 from option code, 2 from option length
}
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (d *DHCPv6) CanDecode() gopacket.LayerClass {
return LayerTypeDHCPv6
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (d *DHCPv6) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error {
dhcp := &DHCPv6{}
err := dhcp.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(dhcp)
return p.NextDecoder(gopacket.LayerTypePayload)
}
// DHCPv6StatusCode represents a DHCP status code - RFC-3315
type DHCPv6StatusCode uint16
// Constants for the DHCPv6StatusCode.
const (
DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota
DHCPv6StatusCodeUnspecFail
DHCPv6StatusCodeNoAddrsAvail
DHCPv6StatusCodeNoBinding
DHCPv6StatusCodeNotOnLink
DHCPv6StatusCodeUseMulticast
)
// String returns a string version of a DHCPv6StatusCode.
func (o DHCPv6StatusCode) String() string {
switch o {
case DHCPv6StatusCodeSuccess:
return "Success"
case DHCPv6StatusCodeUnspecFail:
return "UnspecifiedFailure"
case DHCPv6StatusCodeNoAddrsAvail:
return "NoAddressAvailable"
case DHCPv6StatusCodeNoBinding:
return "NoBinding"
case DHCPv6StatusCodeNotOnLink:
return "NotOnLink"
case DHCPv6StatusCodeUseMulticast:
return "UseMulticast"
default:
return "Unknown"
}
}
// DHCPv6DUIDType represents a DHCP DUID - RFC-3315
type DHCPv6DUIDType uint16
// Constants for the DHCPv6DUIDType.
const (
DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1
DHCPv6DUIDTypeEN
DHCPv6DUIDTypeLL
)
// String returns a string version of a DHCPv6DUIDType.
func (o DHCPv6DUIDType) String() string {
switch o {
case DHCPv6DUIDTypeLLT:
return "LLT"
case DHCPv6DUIDTypeEN:
return "EN"
case DHCPv6DUIDTypeLL:
return "LL"
default:
return "Unknown"
}
}
// DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19)
type DHCPv6DUID struct {
Type DHCPv6DUIDType
// LLT, LL
HardwareType []byte
// EN
EnterpriseNumber []byte
// LLT
Time []byte
// LLT, LL
LinkLayerAddress net.HardwareAddr
// EN
Identifier []byte
}
// DecodeFromBytes decodes the given bytes into a DHCPv6DUID
func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
if len(data) < 2 {
return errors.New("Not enough bytes to decode: " + string(len(data)))
}
d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
d.HardwareType = data[2:4]
}
if d.Type == DHCPv6DUIDTypeLLT {
d.Time = data[4:8]
d.LinkLayerAddress = net.HardwareAddr(data[8:])
} else if d.Type == DHCPv6DUIDTypeEN {
d.EnterpriseNumber = data[2:6]
d.Identifier = data[6:]
} else { // DHCPv6DUIDTypeLL
d.LinkLayerAddress = net.HardwareAddr(data[4:])
}
return nil
}
// Encode encodes the DHCPv6DUID in a slice of bytes
func (d *DHCPv6DUID) Encode() []byte {
length := d.Len()
data := make([]byte, length)
binary.BigEndian.PutUint16(data[0:2], uint16(d.Type))
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
copy(data[2:4], d.HardwareType)
}
if d.Type == DHCPv6DUIDTypeLLT {
copy(data[4:8], d.Time)
copy(data[8:], d.LinkLayerAddress)
} else if d.Type == DHCPv6DUIDTypeEN {
copy(data[2:6], d.EnterpriseNumber)
copy(data[6:], d.Identifier)
} else {
copy(data[4:], d.LinkLayerAddress)
}
return data
}
// Len returns the length of the DHCPv6DUID, respecting the type
func (d *DHCPv6DUID) Len() int {
length := 2 // d.Type
if d.Type == DHCPv6DUIDTypeLLT {
length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress)
} else if d.Type == DHCPv6DUIDTypeEN {
length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier)
} else { // LL
length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress)
}
return length
}
func (d *DHCPv6DUID) String() string {
duid := "Type: " + d.Type.String() + ", "
if d.Type == DHCPv6DUIDTypeLLT {
duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress)
} else if d.Type == DHCPv6DUIDTypeEN {
duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier)
} else { // DHCPv6DUIDTypeLL
duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress)
}
return duid
}
func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) {
duid := &DHCPv6DUID{}
err := duid.DecodeFromBytes(data)
if err != nil {
return nil, err
}
return duid, nil
}