593 lines
16 KiB
Go
593 lines
16 KiB
Go
|
// Copyright 2016 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 (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
|
||
|
"github.com/google/gopacket"
|
||
|
)
|
||
|
|
||
|
// DHCPOp rerprents a bootp operation
|
||
|
type DHCPOp byte
|
||
|
|
||
|
// bootp operations
|
||
|
const (
|
||
|
DHCPOpRequest DHCPOp = 1
|
||
|
DHCPOpReply DHCPOp = 2
|
||
|
)
|
||
|
|
||
|
// String returns a string version of a DHCPOp.
|
||
|
func (o DHCPOp) String() string {
|
||
|
switch o {
|
||
|
case DHCPOpRequest:
|
||
|
return "Request"
|
||
|
case DHCPOpReply:
|
||
|
return "Reply"
|
||
|
default:
|
||
|
return "Unknown"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DHCPMsgType represents a DHCP operation
|
||
|
type DHCPMsgType byte
|
||
|
|
||
|
// Constants that represent DHCP operations
|
||
|
const (
|
||
|
DHCPMsgTypeUnspecified DHCPMsgType = iota
|
||
|
DHCPMsgTypeDiscover
|
||
|
DHCPMsgTypeOffer
|
||
|
DHCPMsgTypeRequest
|
||
|
DHCPMsgTypeDecline
|
||
|
DHCPMsgTypeAck
|
||
|
DHCPMsgTypeNak
|
||
|
DHCPMsgTypeRelease
|
||
|
DHCPMsgTypeInform
|
||
|
)
|
||
|
|
||
|
// String returns a string version of a DHCPMsgType.
|
||
|
func (o DHCPMsgType) String() string {
|
||
|
switch o {
|
||
|
case DHCPMsgTypeUnspecified:
|
||
|
return "Unspecified"
|
||
|
case DHCPMsgTypeDiscover:
|
||
|
return "Discover"
|
||
|
case DHCPMsgTypeOffer:
|
||
|
return "Offer"
|
||
|
case DHCPMsgTypeRequest:
|
||
|
return "Request"
|
||
|
case DHCPMsgTypeDecline:
|
||
|
return "Decline"
|
||
|
case DHCPMsgTypeAck:
|
||
|
return "Ack"
|
||
|
case DHCPMsgTypeNak:
|
||
|
return "Nak"
|
||
|
case DHCPMsgTypeRelease:
|
||
|
return "Release"
|
||
|
case DHCPMsgTypeInform:
|
||
|
return "Inform"
|
||
|
default:
|
||
|
return "Unknown"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//DHCPMagic is the RFC 2131 "magic cooke" for DHCP.
|
||
|
var DHCPMagic uint32 = 0x63825363
|
||
|
|
||
|
// DHCPv4 contains data for a single DHCP packet.
|
||
|
type DHCPv4 struct {
|
||
|
BaseLayer
|
||
|
Operation DHCPOp
|
||
|
HardwareType LinkType
|
||
|
HardwareLen uint8
|
||
|
HardwareOpts uint8
|
||
|
Xid uint32
|
||
|
Secs uint16
|
||
|
Flags uint16
|
||
|
ClientIP net.IP
|
||
|
YourClientIP net.IP
|
||
|
NextServerIP net.IP
|
||
|
RelayAgentIP net.IP
|
||
|
ClientHWAddr net.HardwareAddr
|
||
|
ServerName []byte
|
||
|
File []byte
|
||
|
Options DHCPOptions
|
||
|
}
|
||
|
|
||
|
// DHCPOptions is used to get nicely printed option lists which would normally
|
||
|
// be cut off after 5 options.
|
||
|
type DHCPOptions []DHCPOption
|
||
|
|
||
|
// String returns a string version of the options list.
|
||
|
func (o DHCPOptions) String() string {
|
||
|
buf := &bytes.Buffer{}
|
||
|
buf.WriteByte('[')
|
||
|
for i, opt := range o {
|
||
|
buf.WriteString(opt.String())
|
||
|
if i+1 != len(o) {
|
||
|
buf.WriteString(", ")
|
||
|
}
|
||
|
}
|
||
|
buf.WriteByte(']')
|
||
|
return buf.String()
|
||
|
}
|
||
|
|
||
|
// LayerType returns gopacket.LayerTypeDHCPv4
|
||
|
func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
|
||
|
|
||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||
|
func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||
|
if len(data) < 240 {
|
||
|
df.SetTruncated()
|
||
|
return fmt.Errorf("DHCPv4 length %d too short", len(data))
|
||
|
}
|
||
|
d.Options = d.Options[:0]
|
||
|
d.Operation = DHCPOp(data[0])
|
||
|
d.HardwareType = LinkType(data[1])
|
||
|
d.HardwareLen = data[2]
|
||
|
d.HardwareOpts = data[3]
|
||
|
d.Xid = binary.BigEndian.Uint32(data[4:8])
|
||
|
d.Secs = binary.BigEndian.Uint16(data[8:10])
|
||
|
d.Flags = binary.BigEndian.Uint16(data[10:12])
|
||
|
d.ClientIP = net.IP(data[12:16])
|
||
|
d.YourClientIP = net.IP(data[16:20])
|
||
|
d.NextServerIP = net.IP(data[20:24])
|
||
|
d.RelayAgentIP = net.IP(data[24:28])
|
||
|
d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen])
|
||
|
d.ServerName = data[44:108]
|
||
|
d.File = data[108:236]
|
||
|
if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic {
|
||
|
return InvalidMagicCookie
|
||
|
}
|
||
|
|
||
|
if len(data) <= 240 {
|
||
|
// DHCP Packet could have no option (??)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
options := data[240:]
|
||
|
|
||
|
stop := len(options)
|
||
|
start := 0
|
||
|
for start < stop {
|
||
|
o := DHCPOption{}
|
||
|
if err := o.decode(options[start:]); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if o.Type == DHCPOptEnd {
|
||
|
break
|
||
|
}
|
||
|
d.Options = append(d.Options, o)
|
||
|
// Check if the option is a single byte pad
|
||
|
if o.Type == DHCPOptPad {
|
||
|
start++
|
||
|
} else {
|
||
|
start += int(o.Length) + 2
|
||
|
}
|
||
|
}
|
||
|
|
||
|
d.Contents = data
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Len returns the length of a DHCPv4 packet.
|
||
|
func (d *DHCPv4) Len() uint16 {
|
||
|
n := uint16(240)
|
||
|
for _, o := range d.Options {
|
||
|
if o.Type == DHCPOptPad {
|
||
|
n++
|
||
|
} else {
|
||
|
n += uint16(o.Length) + 2
|
||
|
}
|
||
|
}
|
||
|
n++ // for opt end
|
||
|
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 *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||
|
plen := int(d.Len())
|
||
|
|
||
|
data, err := b.PrependBytes(plen)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
data[0] = byte(d.Operation)
|
||
|
data[1] = byte(d.HardwareType)
|
||
|
if opts.FixLengths {
|
||
|
d.HardwareLen = uint8(len(d.ClientHWAddr))
|
||
|
}
|
||
|
data[2] = d.HardwareLen
|
||
|
data[3] = d.HardwareOpts
|
||
|
binary.BigEndian.PutUint32(data[4:8], d.Xid)
|
||
|
binary.BigEndian.PutUint16(data[8:10], d.Secs)
|
||
|
binary.BigEndian.PutUint16(data[10:12], d.Flags)
|
||
|
copy(data[12:16], d.ClientIP.To4())
|
||
|
copy(data[16:20], d.YourClientIP.To4())
|
||
|
copy(data[20:24], d.NextServerIP.To4())
|
||
|
copy(data[24:28], d.RelayAgentIP.To4())
|
||
|
copy(data[28:44], d.ClientHWAddr)
|
||
|
copy(data[44:108], d.ServerName)
|
||
|
copy(data[108:236], d.File)
|
||
|
binary.BigEndian.PutUint32(data[236:240], DHCPMagic)
|
||
|
|
||
|
if len(d.Options) > 0 {
|
||
|
offset := 240
|
||
|
for _, o := range d.Options {
|
||
|
if err := o.encode(data[offset:]); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// A pad option is only a single byte
|
||
|
if o.Type == DHCPOptPad {
|
||
|
offset++
|
||
|
} else {
|
||
|
offset += 2 + len(o.Data)
|
||
|
}
|
||
|
}
|
||
|
optend := NewDHCPOption(DHCPOptEnd, nil)
|
||
|
if err := optend.encode(data[offset:]); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||
|
func (d *DHCPv4) CanDecode() gopacket.LayerClass {
|
||
|
return LayerTypeDHCPv4
|
||
|
}
|
||
|
|
||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||
|
func (d *DHCPv4) NextLayerType() gopacket.LayerType {
|
||
|
return gopacket.LayerTypePayload
|
||
|
}
|
||
|
|
||
|
func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
|
||
|
dhcp := &DHCPv4{}
|
||
|
err := dhcp.DecodeFromBytes(data, p)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
p.AddLayer(dhcp)
|
||
|
return p.NextDecoder(gopacket.LayerTypePayload)
|
||
|
}
|
||
|
|
||
|
// DHCPOpt represents a DHCP option or parameter from RFC-2132
|
||
|
type DHCPOpt byte
|
||
|
|
||
|
// Constants for the DHCPOpt options.
|
||
|
const (
|
||
|
DHCPOptPad DHCPOpt = 0
|
||
|
DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP
|
||
|
DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC)
|
||
|
DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP
|
||
|
DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP
|
||
|
DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP
|
||
|
DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP
|
||
|
DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP
|
||
|
DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP
|
||
|
DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP
|
||
|
DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP
|
||
|
DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP
|
||
|
DHCPOptHostname DHCPOpt = 12 // n, string
|
||
|
DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16
|
||
|
DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string
|
||
|
DHCPOptDomainName DHCPOpt = 15 // n, string
|
||
|
DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP
|
||
|
DHCPOptRootPath DHCPOpt = 17 // n, string
|
||
|
DHCPOptExtensionsPath DHCPOpt = 18 // n, string
|
||
|
DHCPOptIPForwarding DHCPOpt = 19 // 1, bool
|
||
|
DHCPOptSourceRouting DHCPOpt = 20 // 1, bool
|
||
|
DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP}
|
||
|
DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16
|
||
|
DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte
|
||
|
DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32
|
||
|
DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16
|
||
|
DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16
|
||
|
DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool
|
||
|
DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP
|
||
|
DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool
|
||
|
DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool
|
||
|
DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool
|
||
|
DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP
|
||
|
DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask
|
||
|
DHCPOptARPTrailers DHCPOpt = 34 // 1, bool
|
||
|
DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32
|
||
|
DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool
|
||
|
DHCPOptTCPTTL DHCPOpt = 37 // 1, byte
|
||
|
DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32
|
||
|
DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool
|
||
|
DHCPOptNISDomain DHCPOpt = 40 // n, string
|
||
|
DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP
|
||
|
DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP
|
||
|
DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated.
|
||
|
DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP
|
||
|
DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP
|
||
|
DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte
|
||
|
DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string
|
||
|
DHCPOptXFontServer DHCPOpt = 48 // n, string
|
||
|
DHCPOptXDisplayManager DHCPOpt = 49 // n, string
|
||
|
DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP
|
||
|
DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32
|
||
|
DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3
|
||
|
DHCPOptMessageType DHCPOpt = 53 // 1, 1-7
|
||
|
DHCPOptServerID DHCPOpt = 54 // 4, net.IP
|
||
|
DHCPOptParamsRequest DHCPOpt = 55 // n, []byte
|
||
|
DHCPOptMessage DHCPOpt = 56 // n, 3
|
||
|
DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16
|
||
|
DHCPOptT1 DHCPOpt = 58 // 4, uint32
|
||
|
DHCPOptT2 DHCPOpt = 59 // 4, uint32
|
||
|
DHCPOptClassID DHCPOpt = 60 // n, []byte
|
||
|
DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte
|
||
|
DHCPOptDomainSearch DHCPOpt = 119 // n, string
|
||
|
DHCPOptSIPServers DHCPOpt = 120 // n, url
|
||
|
DHCPOptClasslessStaticRoute DHCPOpt = 121 //
|
||
|
DHCPOptEnd DHCPOpt = 255
|
||
|
)
|
||
|
|
||
|
// String returns a string version of a DHCPOpt.
|
||
|
func (o DHCPOpt) String() string {
|
||
|
switch o {
|
||
|
case DHCPOptPad:
|
||
|
return "(padding)"
|
||
|
case DHCPOptSubnetMask:
|
||
|
return "SubnetMask"
|
||
|
case DHCPOptTimeOffset:
|
||
|
return "TimeOffset"
|
||
|
case DHCPOptRouter:
|
||
|
return "Router"
|
||
|
case DHCPOptTimeServer:
|
||
|
return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP
|
||
|
case DHCPOptNameServer:
|
||
|
return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS
|
||
|
case DHCPOptDNS:
|
||
|
return "DNS"
|
||
|
case DHCPOptLogServer:
|
||
|
return "mitLCS" // MIT LCS server protocol yada yada w. Syslog
|
||
|
case DHCPOptCookieServer:
|
||
|
return "CookieServer"
|
||
|
case DHCPOptLPRServer:
|
||
|
return "LPRServer"
|
||
|
case DHCPOptImpressServer:
|
||
|
return "ImpressServer"
|
||
|
case DHCPOptResLocServer:
|
||
|
return "ResourceLocationServer"
|
||
|
case DHCPOptHostname:
|
||
|
return "Hostname"
|
||
|
case DHCPOptBootfileSize:
|
||
|
return "BootfileSize"
|
||
|
case DHCPOptMeritDumpFile:
|
||
|
return "MeritDumpFile"
|
||
|
case DHCPOptDomainName:
|
||
|
return "DomainName"
|
||
|
case DHCPOptSwapServer:
|
||
|
return "SwapServer"
|
||
|
case DHCPOptRootPath:
|
||
|
return "RootPath"
|
||
|
case DHCPOptExtensionsPath:
|
||
|
return "ExtensionsPath"
|
||
|
case DHCPOptIPForwarding:
|
||
|
return "IPForwarding"
|
||
|
case DHCPOptSourceRouting:
|
||
|
return "SourceRouting"
|
||
|
case DHCPOptPolicyFilter:
|
||
|
return "PolicyFilter"
|
||
|
case DHCPOptDatagramMTU:
|
||
|
return "DatagramMTU"
|
||
|
case DHCPOptDefaultTTL:
|
||
|
return "DefaultTTL"
|
||
|
case DHCPOptPathMTUAgingTimeout:
|
||
|
return "PathMTUAgingTimeout"
|
||
|
case DHCPOptPathPlateuTableOption:
|
||
|
return "PathPlateuTableOption"
|
||
|
case DHCPOptInterfaceMTU:
|
||
|
return "InterfaceMTU"
|
||
|
case DHCPOptAllSubsLocal:
|
||
|
return "AllSubsLocal"
|
||
|
case DHCPOptBroadcastAddr:
|
||
|
return "BroadcastAddress"
|
||
|
case DHCPOptMaskDiscovery:
|
||
|
return "MaskDiscovery"
|
||
|
case DHCPOptMaskSupplier:
|
||
|
return "MaskSupplier"
|
||
|
case DHCPOptRouterDiscovery:
|
||
|
return "RouterDiscovery"
|
||
|
case DHCPOptSolicitAddr:
|
||
|
return "SolicitAddr"
|
||
|
case DHCPOptStaticRoute:
|
||
|
return "StaticRoute"
|
||
|
case DHCPOptARPTrailers:
|
||
|
return "ARPTrailers"
|
||
|
case DHCPOptARPTimeout:
|
||
|
return "ARPTimeout"
|
||
|
case DHCPOptEthernetEncap:
|
||
|
return "EthernetEncap"
|
||
|
case DHCPOptTCPTTL:
|
||
|
return "TCPTTL"
|
||
|
case DHCPOptTCPKeepAliveInt:
|
||
|
return "TCPKeepAliveInt"
|
||
|
case DHCPOptTCPKeepAliveGarbage:
|
||
|
return "TCPKeepAliveGarbage"
|
||
|
case DHCPOptNISDomain:
|
||
|
return "NISDomain"
|
||
|
case DHCPOptNISServers:
|
||
|
return "NISServers"
|
||
|
case DHCPOptNTPServers:
|
||
|
return "NTPServers"
|
||
|
case DHCPOptVendorOption:
|
||
|
return "VendorOption"
|
||
|
case DHCPOptNetBIOSTCPNS:
|
||
|
return "NetBIOSOverTCPNS"
|
||
|
case DHCPOptNetBIOSTCPDDS:
|
||
|
return "NetBiosOverTCPDDS"
|
||
|
case DHCPOptNETBIOSTCPNodeType:
|
||
|
return "NetBIOSOverTCPNodeType"
|
||
|
case DHCPOptNetBIOSTCPScope:
|
||
|
return "NetBIOSOverTCPScope"
|
||
|
case DHCPOptXFontServer:
|
||
|
return "XFontServer"
|
||
|
case DHCPOptXDisplayManager:
|
||
|
return "XDisplayManager"
|
||
|
case DHCPOptEnd:
|
||
|
return "(end)"
|
||
|
case DHCPOptSIPServers:
|
||
|
return "SipServers"
|
||
|
case DHCPOptRequestIP:
|
||
|
return "RequestIP"
|
||
|
case DHCPOptLeaseTime:
|
||
|
return "LeaseTime"
|
||
|
case DHCPOptExtOptions:
|
||
|
return "ExtOpts"
|
||
|
case DHCPOptMessageType:
|
||
|
return "MessageType"
|
||
|
case DHCPOptServerID:
|
||
|
return "ServerID"
|
||
|
case DHCPOptParamsRequest:
|
||
|
return "ParamsRequest"
|
||
|
case DHCPOptMessage:
|
||
|
return "Message"
|
||
|
case DHCPOptMaxMessageSize:
|
||
|
return "MaxDHCPSize"
|
||
|
case DHCPOptT1:
|
||
|
return "Timer1"
|
||
|
case DHCPOptT2:
|
||
|
return "Timer2"
|
||
|
case DHCPOptClassID:
|
||
|
return "ClassID"
|
||
|
case DHCPOptClientID:
|
||
|
return "ClientID"
|
||
|
case DHCPOptDomainSearch:
|
||
|
return "DomainSearch"
|
||
|
case DHCPOptClasslessStaticRoute:
|
||
|
return "ClasslessStaticRoute"
|
||
|
default:
|
||
|
return "Unknown"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DHCPOption rerpresents a DHCP option.
|
||
|
type DHCPOption struct {
|
||
|
Type DHCPOpt
|
||
|
Length uint8
|
||
|
Data []byte
|
||
|
}
|
||
|
|
||
|
// String returns a string version of a DHCP Option.
|
||
|
func (o DHCPOption) String() string {
|
||
|
switch o.Type {
|
||
|
|
||
|
case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath,
|
||
|
DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer,
|
||
|
DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string
|
||
|
return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data))
|
||
|
|
||
|
case DHCPOptMessageType:
|
||
|
if len(o.Data) != 1 {
|
||
|
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||
|
}
|
||
|
return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0]))
|
||
|
|
||
|
case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr,
|
||
|
DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP
|
||
|
if len(o.Data) < 4 {
|
||
|
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||
|
}
|
||
|
return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data))
|
||
|
|
||
|
case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout,
|
||
|
DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32
|
||
|
if len(o.Data) != 4 {
|
||
|
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||
|
}
|
||
|
return fmt.Sprintf("Option(%s:%d)", o.Type,
|
||
|
uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3]))
|
||
|
|
||
|
case DHCPOptParamsRequest:
|
||
|
buf := &bytes.Buffer{}
|
||
|
buf.WriteString(fmt.Sprintf("Option(%s:", o.Type))
|
||
|
for i, v := range o.Data {
|
||
|
buf.WriteString(DHCPOpt(v).String())
|
||
|
if i+1 != len(o.Data) {
|
||
|
buf.WriteByte(',')
|
||
|
}
|
||
|
}
|
||
|
buf.WriteString(")")
|
||
|
return buf.String()
|
||
|
|
||
|
default:
|
||
|
return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewDHCPOption constructs a new DHCPOption with a given type and data.
|
||
|
func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption {
|
||
|
o := DHCPOption{Type: t}
|
||
|
if data != nil {
|
||
|
o.Data = data
|
||
|
o.Length = uint8(len(data))
|
||
|
}
|
||
|
return o
|
||
|
}
|
||
|
|
||
|
func (o *DHCPOption) encode(b []byte) error {
|
||
|
switch o.Type {
|
||
|
case DHCPOptPad, DHCPOptEnd:
|
||
|
b[0] = byte(o.Type)
|
||
|
default:
|
||
|
b[0] = byte(o.Type)
|
||
|
b[1] = o.Length
|
||
|
copy(b[2:], o.Data)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (o *DHCPOption) decode(data []byte) error {
|
||
|
if len(data) < 1 {
|
||
|
// Pad/End have a length of 1
|
||
|
return DecOptionNotEnoughData
|
||
|
}
|
||
|
o.Type = DHCPOpt(data[0])
|
||
|
switch o.Type {
|
||
|
case DHCPOptPad, DHCPOptEnd:
|
||
|
o.Data = nil
|
||
|
default:
|
||
|
if len(data) < 2 {
|
||
|
return DecOptionNotEnoughData
|
||
|
}
|
||
|
o.Length = data[1]
|
||
|
if int(o.Length) > len(data[2:]) {
|
||
|
return DecOptionMalformed
|
||
|
}
|
||
|
o.Data = data[2 : 2+int(o.Length)]
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts.
|
||
|
type DHCPv4Error string
|
||
|
|
||
|
// DHCPv4Error implements error interface.
|
||
|
func (d DHCPv4Error) Error() string {
|
||
|
return string(d)
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
// DecOptionNotEnoughData is returned when there is not enough data during option's decode process
|
||
|
DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode")
|
||
|
// DecOptionMalformed is returned when the option is malformed
|
||
|
DecOptionMalformed = DHCPv4Error("Option is malformed")
|
||
|
// InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header
|
||
|
InvalidMagicCookie = DHCPv4Error("Bad DHCP header")
|
||
|
)
|