github.com/gopacket/gopacket@v1.1.0/layers/dhcpv4.go (about)

     1  // Copyright 2016 Google, Inc. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style license
     4  // that can be found in the LICENSE file in the root of the source
     5  // tree.
     6  
     7  package layers
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/binary"
    12  	"fmt"
    13  	"net"
    14  
    15  	"github.com/gopacket/gopacket"
    16  )
    17  
    18  // DHCPOp rerprents a bootp operation
    19  type DHCPOp byte
    20  
    21  // bootp operations
    22  const (
    23  	DHCPOpRequest DHCPOp = 1
    24  	DHCPOpReply   DHCPOp = 2
    25  )
    26  
    27  // String returns a string version of a DHCPOp.
    28  func (o DHCPOp) String() string {
    29  	switch o {
    30  	case DHCPOpRequest:
    31  		return "Request"
    32  	case DHCPOpReply:
    33  		return "Reply"
    34  	default:
    35  		return "Unknown"
    36  	}
    37  }
    38  
    39  // DHCPMsgType represents a DHCP operation
    40  type DHCPMsgType byte
    41  
    42  // Constants that represent DHCP operations
    43  const (
    44  	DHCPMsgTypeUnspecified DHCPMsgType = iota
    45  	DHCPMsgTypeDiscover
    46  	DHCPMsgTypeOffer
    47  	DHCPMsgTypeRequest
    48  	DHCPMsgTypeDecline
    49  	DHCPMsgTypeAck
    50  	DHCPMsgTypeNak
    51  	DHCPMsgTypeRelease
    52  	DHCPMsgTypeInform
    53  )
    54  
    55  // String returns a string version of a DHCPMsgType.
    56  func (o DHCPMsgType) String() string {
    57  	switch o {
    58  	case DHCPMsgTypeUnspecified:
    59  		return "Unspecified"
    60  	case DHCPMsgTypeDiscover:
    61  		return "Discover"
    62  	case DHCPMsgTypeOffer:
    63  		return "Offer"
    64  	case DHCPMsgTypeRequest:
    65  		return "Request"
    66  	case DHCPMsgTypeDecline:
    67  		return "Decline"
    68  	case DHCPMsgTypeAck:
    69  		return "Ack"
    70  	case DHCPMsgTypeNak:
    71  		return "Nak"
    72  	case DHCPMsgTypeRelease:
    73  		return "Release"
    74  	case DHCPMsgTypeInform:
    75  		return "Inform"
    76  	default:
    77  		return "Unknown"
    78  	}
    79  }
    80  
    81  // DHCPMagic is the RFC 2131 "magic cooke" for DHCP.
    82  var DHCPMagic uint32 = 0x63825363
    83  
    84  // DHCPv4 contains data for a single DHCP packet.
    85  type DHCPv4 struct {
    86  	BaseLayer
    87  	Operation    DHCPOp
    88  	HardwareType LinkType
    89  	HardwareLen  uint8
    90  	HardwareOpts uint8
    91  	Xid          uint32
    92  	Secs         uint16
    93  	Flags        uint16
    94  	ClientIP     net.IP
    95  	YourClientIP net.IP
    96  	NextServerIP net.IP
    97  	RelayAgentIP net.IP
    98  	ClientHWAddr net.HardwareAddr
    99  	ServerName   []byte
   100  	File         []byte
   101  	Options      DHCPOptions
   102  }
   103  
   104  // DHCPOptions is used to get nicely printed option lists which would normally
   105  // be cut off after 5 options.
   106  type DHCPOptions []DHCPOption
   107  
   108  // String returns a string version of the options list.
   109  func (o DHCPOptions) String() string {
   110  	buf := &bytes.Buffer{}
   111  	buf.WriteByte('[')
   112  	for i, opt := range o {
   113  		buf.WriteString(opt.String())
   114  		if i+1 != len(o) {
   115  			buf.WriteString(", ")
   116  		}
   117  	}
   118  	buf.WriteByte(']')
   119  	return buf.String()
   120  }
   121  
   122  // LayerType returns gopacket.LayerTypeDHCPv4
   123  func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
   124  
   125  // DecodeFromBytes decodes the given bytes into this layer.
   126  func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
   127  	if len(data) < 240 {
   128  		df.SetTruncated()
   129  		return fmt.Errorf("DHCPv4 length %d too short", len(data))
   130  	}
   131  	d.Options = d.Options[:0]
   132  	d.Operation = DHCPOp(data[0])
   133  	d.HardwareType = LinkType(data[1])
   134  	d.HardwareLen = data[2]
   135  	d.HardwareOpts = data[3]
   136  	d.Xid = binary.BigEndian.Uint32(data[4:8])
   137  	d.Secs = binary.BigEndian.Uint16(data[8:10])
   138  	d.Flags = binary.BigEndian.Uint16(data[10:12])
   139  	d.ClientIP = net.IP(data[12:16])
   140  	d.YourClientIP = net.IP(data[16:20])
   141  	d.NextServerIP = net.IP(data[20:24])
   142  	d.RelayAgentIP = net.IP(data[24:28])
   143  	d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen])
   144  	d.ServerName = data[44:108]
   145  	d.File = data[108:236]
   146  	if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic {
   147  		return InvalidMagicCookie
   148  	}
   149  
   150  	if len(data) <= 240 {
   151  		// DHCP Packet could have no option (??)
   152  		return nil
   153  	}
   154  
   155  	options := data[240:]
   156  
   157  	stop := len(options)
   158  	start := 0
   159  	for start < stop {
   160  		o := DHCPOption{}
   161  		if err := o.decode(options[start:]); err != nil {
   162  			return err
   163  		}
   164  		if o.Type == DHCPOptEnd {
   165  			break
   166  		}
   167  		d.Options = append(d.Options, o)
   168  		// Check if the option is a single byte pad
   169  		if o.Type == DHCPOptPad {
   170  			start++
   171  		} else {
   172  			start += int(o.Length) + 2
   173  		}
   174  	}
   175  
   176  	d.Contents = data
   177  
   178  	return nil
   179  }
   180  
   181  // Len returns the length of a DHCPv4 packet.
   182  func (d *DHCPv4) Len() uint16 {
   183  	n := uint16(240)
   184  	for _, o := range d.Options {
   185  		if o.Type == DHCPOptPad {
   186  			n++
   187  		} else {
   188  			n += uint16(o.Length) + 2
   189  		}
   190  	}
   191  	n++ // for opt end
   192  	return n
   193  }
   194  
   195  // SerializeTo writes the serialized form of this layer into the
   196  // SerializationBuffer, implementing gopacket.SerializableLayer.
   197  // See the docs for gopacket.SerializableLayer for more info.
   198  func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
   199  	plen := int(d.Len())
   200  
   201  	data, err := b.PrependBytes(plen)
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	data[0] = byte(d.Operation)
   207  	data[1] = byte(d.HardwareType)
   208  	if opts.FixLengths {
   209  		d.HardwareLen = uint8(len(d.ClientHWAddr))
   210  	}
   211  	data[2] = d.HardwareLen
   212  	data[3] = d.HardwareOpts
   213  	binary.BigEndian.PutUint32(data[4:8], d.Xid)
   214  	binary.BigEndian.PutUint16(data[8:10], d.Secs)
   215  	binary.BigEndian.PutUint16(data[10:12], d.Flags)
   216  	copy(data[12:16], d.ClientIP.To4())
   217  	copy(data[16:20], d.YourClientIP.To4())
   218  	copy(data[20:24], d.NextServerIP.To4())
   219  	copy(data[24:28], d.RelayAgentIP.To4())
   220  	copy(data[28:44], d.ClientHWAddr)
   221  	copy(data[44:108], d.ServerName)
   222  	copy(data[108:236], d.File)
   223  	binary.BigEndian.PutUint32(data[236:240], DHCPMagic)
   224  
   225  	if len(d.Options) > 0 {
   226  		offset := 240
   227  		for _, o := range d.Options {
   228  			if err := o.encode(data[offset:]); err != nil {
   229  				return err
   230  			}
   231  			// A pad option is only a single byte
   232  			if o.Type == DHCPOptPad {
   233  				offset++
   234  			} else {
   235  				offset += 2 + len(o.Data)
   236  			}
   237  		}
   238  		optend := NewDHCPOption(DHCPOptEnd, nil)
   239  		if err := optend.encode(data[offset:]); err != nil {
   240  			return err
   241  		}
   242  	}
   243  	return nil
   244  }
   245  
   246  // CanDecode returns the set of layer types that this DecodingLayer can decode.
   247  func (d *DHCPv4) CanDecode() gopacket.LayerClass {
   248  	return LayerTypeDHCPv4
   249  }
   250  
   251  // NextLayerType returns the layer type contained by this DecodingLayer.
   252  func (d *DHCPv4) NextLayerType() gopacket.LayerType {
   253  	return gopacket.LayerTypePayload
   254  }
   255  
   256  func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
   257  	dhcp := &DHCPv4{}
   258  	err := dhcp.DecodeFromBytes(data, p)
   259  	if err != nil {
   260  		return err
   261  	}
   262  	p.AddLayer(dhcp)
   263  	return p.NextDecoder(gopacket.LayerTypePayload)
   264  }
   265  
   266  // DHCPOpt represents a DHCP option or parameter from RFC-2132
   267  type DHCPOpt byte
   268  
   269  // Constants for the DHCPOpt options.
   270  const (
   271  	DHCPOptPad                   DHCPOpt = 0
   272  	DHCPOptSubnetMask            DHCPOpt = 1   // 4, net.IP
   273  	DHCPOptTimeOffset            DHCPOpt = 2   // 4, int32 (signed seconds from UTC)
   274  	DHCPOptRouter                DHCPOpt = 3   // n*4, [n]net.IP
   275  	DHCPOptTimeServer            DHCPOpt = 4   // n*4, [n]net.IP
   276  	DHCPOptNameServer            DHCPOpt = 5   // n*4, [n]net.IP
   277  	DHCPOptDNS                   DHCPOpt = 6   // n*4, [n]net.IP
   278  	DHCPOptLogServer             DHCPOpt = 7   // n*4, [n]net.IP
   279  	DHCPOptCookieServer          DHCPOpt = 8   // n*4, [n]net.IP
   280  	DHCPOptLPRServer             DHCPOpt = 9   // n*4, [n]net.IP
   281  	DHCPOptImpressServer         DHCPOpt = 10  // n*4, [n]net.IP
   282  	DHCPOptResLocServer          DHCPOpt = 11  // n*4, [n]net.IP
   283  	DHCPOptHostname              DHCPOpt = 12  // n, string
   284  	DHCPOptBootfileSize          DHCPOpt = 13  // 2, uint16
   285  	DHCPOptMeritDumpFile         DHCPOpt = 14  // >1, string
   286  	DHCPOptDomainName            DHCPOpt = 15  // n, string
   287  	DHCPOptSwapServer            DHCPOpt = 16  // n*4, [n]net.IP
   288  	DHCPOptRootPath              DHCPOpt = 17  // n, string
   289  	DHCPOptExtensionsPath        DHCPOpt = 18  // n, string
   290  	DHCPOptIPForwarding          DHCPOpt = 19  // 1, bool
   291  	DHCPOptSourceRouting         DHCPOpt = 20  // 1, bool
   292  	DHCPOptPolicyFilter          DHCPOpt = 21  // 8*n, [n]{net.IP/net.IP}
   293  	DHCPOptDatagramMTU           DHCPOpt = 22  // 2, uint16
   294  	DHCPOptDefaultTTL            DHCPOpt = 23  // 1, byte
   295  	DHCPOptPathMTUAgingTimeout   DHCPOpt = 24  // 4, uint32
   296  	DHCPOptPathPlateuTableOption DHCPOpt = 25  // 2*n, []uint16
   297  	DHCPOptInterfaceMTU          DHCPOpt = 26  // 2, uint16
   298  	DHCPOptAllSubsLocal          DHCPOpt = 27  // 1, bool
   299  	DHCPOptBroadcastAddr         DHCPOpt = 28  // 4, net.IP
   300  	DHCPOptMaskDiscovery         DHCPOpt = 29  // 1, bool
   301  	DHCPOptMaskSupplier          DHCPOpt = 30  // 1, bool
   302  	DHCPOptRouterDiscovery       DHCPOpt = 31  // 1, bool
   303  	DHCPOptSolicitAddr           DHCPOpt = 32  // 4, net.IP
   304  	DHCPOptStaticRoute           DHCPOpt = 33  // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask
   305  	DHCPOptARPTrailers           DHCPOpt = 34  // 1, bool
   306  	DHCPOptARPTimeout            DHCPOpt = 35  // 4, uint32
   307  	DHCPOptEthernetEncap         DHCPOpt = 36  // 1, bool
   308  	DHCPOptTCPTTL                DHCPOpt = 37  // 1, byte
   309  	DHCPOptTCPKeepAliveInt       DHCPOpt = 38  // 4, uint32
   310  	DHCPOptTCPKeepAliveGarbage   DHCPOpt = 39  // 1, bool
   311  	DHCPOptNISDomain             DHCPOpt = 40  // n, string
   312  	DHCPOptNISServers            DHCPOpt = 41  // 4*n,  [n]net.IP
   313  	DHCPOptNTPServers            DHCPOpt = 42  // 4*n, [n]net.IP
   314  	DHCPOptVendorOption          DHCPOpt = 43  // n, [n]byte // may be encapsulated.
   315  	DHCPOptNetBIOSTCPNS          DHCPOpt = 44  // 4*n, [n]net.IP
   316  	DHCPOptNetBIOSTCPDDS         DHCPOpt = 45  // 4*n, [n]net.IP
   317  	DHCPOptNETBIOSTCPNodeType    DHCPOpt = 46  // 1, magic byte
   318  	DHCPOptNetBIOSTCPScope       DHCPOpt = 47  // n, string
   319  	DHCPOptXFontServer           DHCPOpt = 48  // n, string
   320  	DHCPOptXDisplayManager       DHCPOpt = 49  // n, string
   321  	DHCPOptRequestIP             DHCPOpt = 50  // 4, net.IP
   322  	DHCPOptLeaseTime             DHCPOpt = 51  // 4, uint32
   323  	DHCPOptExtOptions            DHCPOpt = 52  // 1, 1/2/3
   324  	DHCPOptMessageType           DHCPOpt = 53  // 1, 1-7
   325  	DHCPOptServerID              DHCPOpt = 54  // 4, net.IP
   326  	DHCPOptParamsRequest         DHCPOpt = 55  // n, []byte
   327  	DHCPOptMessage               DHCPOpt = 56  // n, 3
   328  	DHCPOptMaxMessageSize        DHCPOpt = 57  // 2, uint16
   329  	DHCPOptT1                    DHCPOpt = 58  // 4, uint32
   330  	DHCPOptT2                    DHCPOpt = 59  // 4, uint32
   331  	DHCPOptClassID               DHCPOpt = 60  // n, []byte
   332  	DHCPOptClientID              DHCPOpt = 61  // n >=  2, []byte
   333  	DHCPOptDomainSearch          DHCPOpt = 119 // n, string
   334  	DHCPOptSIPServers            DHCPOpt = 120 // n, url
   335  	DHCPOptClasslessStaticRoute  DHCPOpt = 121 //
   336  	DHCPOptMUDURLV4              DHCPOpt = 161 // n, string
   337  	DHCPOptEnd                   DHCPOpt = 255
   338  )
   339  
   340  // String returns a string version of a DHCPOpt.
   341  func (o DHCPOpt) String() string {
   342  	switch o {
   343  	case DHCPOptPad:
   344  		return "(padding)"
   345  	case DHCPOptSubnetMask:
   346  		return "SubnetMask"
   347  	case DHCPOptTimeOffset:
   348  		return "TimeOffset"
   349  	case DHCPOptRouter:
   350  		return "Router"
   351  	case DHCPOptTimeServer:
   352  		return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP
   353  	case DHCPOptNameServer:
   354  		return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS
   355  	case DHCPOptDNS:
   356  		return "DNS"
   357  	case DHCPOptLogServer:
   358  		return "mitLCS" // MIT LCS server protocol yada yada w. Syslog
   359  	case DHCPOptCookieServer:
   360  		return "CookieServer"
   361  	case DHCPOptLPRServer:
   362  		return "LPRServer"
   363  	case DHCPOptImpressServer:
   364  		return "ImpressServer"
   365  	case DHCPOptResLocServer:
   366  		return "ResourceLocationServer"
   367  	case DHCPOptHostname:
   368  		return "Hostname"
   369  	case DHCPOptBootfileSize:
   370  		return "BootfileSize"
   371  	case DHCPOptMeritDumpFile:
   372  		return "MeritDumpFile"
   373  	case DHCPOptDomainName:
   374  		return "DomainName"
   375  	case DHCPOptSwapServer:
   376  		return "SwapServer"
   377  	case DHCPOptRootPath:
   378  		return "RootPath"
   379  	case DHCPOptExtensionsPath:
   380  		return "ExtensionsPath"
   381  	case DHCPOptIPForwarding:
   382  		return "IPForwarding"
   383  	case DHCPOptSourceRouting:
   384  		return "SourceRouting"
   385  	case DHCPOptPolicyFilter:
   386  		return "PolicyFilter"
   387  	case DHCPOptDatagramMTU:
   388  		return "DatagramMTU"
   389  	case DHCPOptDefaultTTL:
   390  		return "DefaultTTL"
   391  	case DHCPOptPathMTUAgingTimeout:
   392  		return "PathMTUAgingTimeout"
   393  	case DHCPOptPathPlateuTableOption:
   394  		return "PathPlateuTableOption"
   395  	case DHCPOptInterfaceMTU:
   396  		return "InterfaceMTU"
   397  	case DHCPOptAllSubsLocal:
   398  		return "AllSubsLocal"
   399  	case DHCPOptBroadcastAddr:
   400  		return "BroadcastAddress"
   401  	case DHCPOptMaskDiscovery:
   402  		return "MaskDiscovery"
   403  	case DHCPOptMaskSupplier:
   404  		return "MaskSupplier"
   405  	case DHCPOptRouterDiscovery:
   406  		return "RouterDiscovery"
   407  	case DHCPOptSolicitAddr:
   408  		return "SolicitAddr"
   409  	case DHCPOptStaticRoute:
   410  		return "StaticRoute"
   411  	case DHCPOptARPTrailers:
   412  		return "ARPTrailers"
   413  	case DHCPOptARPTimeout:
   414  		return "ARPTimeout"
   415  	case DHCPOptEthernetEncap:
   416  		return "EthernetEncap"
   417  	case DHCPOptTCPTTL:
   418  		return "TCPTTL"
   419  	case DHCPOptTCPKeepAliveInt:
   420  		return "TCPKeepAliveInt"
   421  	case DHCPOptTCPKeepAliveGarbage:
   422  		return "TCPKeepAliveGarbage"
   423  	case DHCPOptNISDomain:
   424  		return "NISDomain"
   425  	case DHCPOptNISServers:
   426  		return "NISServers"
   427  	case DHCPOptNTPServers:
   428  		return "NTPServers"
   429  	case DHCPOptVendorOption:
   430  		return "VendorOption"
   431  	case DHCPOptNetBIOSTCPNS:
   432  		return "NetBIOSOverTCPNS"
   433  	case DHCPOptNetBIOSTCPDDS:
   434  		return "NetBiosOverTCPDDS"
   435  	case DHCPOptNETBIOSTCPNodeType:
   436  		return "NetBIOSOverTCPNodeType"
   437  	case DHCPOptNetBIOSTCPScope:
   438  		return "NetBIOSOverTCPScope"
   439  	case DHCPOptXFontServer:
   440  		return "XFontServer"
   441  	case DHCPOptXDisplayManager:
   442  		return "XDisplayManager"
   443  	case DHCPOptEnd:
   444  		return "(end)"
   445  	case DHCPOptSIPServers:
   446  		return "SipServers"
   447  	case DHCPOptRequestIP:
   448  		return "RequestIP"
   449  	case DHCPOptLeaseTime:
   450  		return "LeaseTime"
   451  	case DHCPOptExtOptions:
   452  		return "ExtOpts"
   453  	case DHCPOptMessageType:
   454  		return "MessageType"
   455  	case DHCPOptServerID:
   456  		return "ServerID"
   457  	case DHCPOptParamsRequest:
   458  		return "ParamsRequest"
   459  	case DHCPOptMessage:
   460  		return "Message"
   461  	case DHCPOptMaxMessageSize:
   462  		return "MaxDHCPSize"
   463  	case DHCPOptT1:
   464  		return "Timer1"
   465  	case DHCPOptT2:
   466  		return "Timer2"
   467  	case DHCPOptClassID:
   468  		return "ClassID"
   469  	case DHCPOptClientID:
   470  		return "ClientID"
   471  	case DHCPOptDomainSearch:
   472  		return "DomainSearch"
   473  	case DHCPOptClasslessStaticRoute:
   474  		return "ClasslessStaticRoute"
   475  	case DHCPOptMUDURLV4:
   476  		return "ManufacturerUsageDescriptionURL"
   477  	default:
   478  		return "Unknown"
   479  	}
   480  }
   481  
   482  // DHCPOption rerpresents a DHCP option.
   483  type DHCPOption struct {
   484  	Type   DHCPOpt
   485  	Length uint8
   486  	Data   []byte
   487  }
   488  
   489  // String returns a string version of a DHCP Option.
   490  func (o DHCPOption) String() string {
   491  	switch o.Type {
   492  
   493  	case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath,
   494  		DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer,
   495  		DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string
   496  		return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data))
   497  
   498  	case DHCPOptMessageType:
   499  		if len(o.Data) != 1 {
   500  			return fmt.Sprintf("Option(%s:INVALID)", o.Type)
   501  		}
   502  		return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0]))
   503  
   504  	case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr,
   505  		DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP
   506  		if len(o.Data) < 4 {
   507  			return fmt.Sprintf("Option(%s:INVALID)", o.Type)
   508  		}
   509  		return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data))
   510  
   511  	case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout,
   512  		DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32
   513  		if len(o.Data) != 4 {
   514  			return fmt.Sprintf("Option(%s:INVALID)", o.Type)
   515  		}
   516  		return fmt.Sprintf("Option(%s:%d)", o.Type,
   517  			uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3]))
   518  
   519  	case DHCPOptParamsRequest:
   520  		buf := &bytes.Buffer{}
   521  		buf.WriteString(fmt.Sprintf("Option(%s:", o.Type))
   522  		for i, v := range o.Data {
   523  			buf.WriteString(DHCPOpt(v).String())
   524  			if i+1 != len(o.Data) {
   525  				buf.WriteByte(',')
   526  			}
   527  		}
   528  		buf.WriteString(")")
   529  		return buf.String()
   530  
   531  	default:
   532  		return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
   533  	}
   534  }
   535  
   536  // NewDHCPOption constructs a new DHCPOption with a given type and data.
   537  func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption {
   538  	o := DHCPOption{Type: t}
   539  	if data != nil {
   540  		o.Data = data
   541  		o.Length = uint8(len(data))
   542  	}
   543  	return o
   544  }
   545  
   546  func (o *DHCPOption) encode(b []byte) error {
   547  	switch o.Type {
   548  	case DHCPOptPad, DHCPOptEnd:
   549  		b[0] = byte(o.Type)
   550  	default:
   551  		b[0] = byte(o.Type)
   552  		b[1] = o.Length
   553  		copy(b[2:], o.Data)
   554  	}
   555  	return nil
   556  }
   557  
   558  func (o *DHCPOption) decode(data []byte) error {
   559  	if len(data) < 1 {
   560  		// Pad/End have a length of 1
   561  		return DecOptionNotEnoughData
   562  	}
   563  	o.Type = DHCPOpt(data[0])
   564  	switch o.Type {
   565  	case DHCPOptPad, DHCPOptEnd:
   566  		o.Data = nil
   567  	default:
   568  		if len(data) < 2 {
   569  			return DecOptionNotEnoughData
   570  		}
   571  		o.Length = data[1]
   572  		if int(o.Length) > len(data[2:]) {
   573  			return DecOptionMalformed
   574  		}
   575  		o.Data = data[2 : 2+int(o.Length)]
   576  	}
   577  	return nil
   578  }
   579  
   580  // DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts.
   581  type DHCPv4Error string
   582  
   583  // DHCPv4Error implements error interface.
   584  func (d DHCPv4Error) Error() string {
   585  	return string(d)
   586  }
   587  
   588  const (
   589  	// DecOptionNotEnoughData is returned when there is not enough data during option's decode process
   590  	DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode")
   591  	// DecOptionMalformed is returned when the option is malformed
   592  	DecOptionMalformed = DHCPv4Error("Option is malformed")
   593  	// InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header
   594  	InvalidMagicCookie = DHCPv4Error("Bad DHCP header")
   595  )