// 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" "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 { if len(data) < 4 { df.SetTruncated() return fmt.Errorf("DHCPv6 length %d too short", len(data)) } d.BaseLayer = BaseLayer{Contents: data} d.Options = d.Options[:0] d.MsgType = DHCPv6MsgType(data[0]) offset := 0 if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply { if len(data) < 34 { df.SetTruncated() return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType) } 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 fmt.Errorf("Not enough bytes to decode: %d", len(data)) } d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2])) if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL { if len(data) < 4 { return fmt.Errorf("Not enough bytes to decode: %d", len(data)) } d.HardwareType = data[2:4] } if d.Type == DHCPv6DUIDTypeLLT { if len(data) < 8 { return fmt.Errorf("Not enough bytes to decode: %d", len(data)) } d.Time = data[4:8] d.LinkLayerAddress = net.HardwareAddr(data[8:]) } else if d.Type == DHCPv6DUIDTypeEN { if len(data) < 6 { return fmt.Errorf("Not enough bytes to decode: %d", len(data)) } d.EnterpriseNumber = data[2:6] d.Identifier = data[6:] } else { // DHCPv6DUIDTypeLL if len(data) < 4 { return fmt.Errorf("Not enough bytes to decode: %d", len(data)) } 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 }