github.com/contiv/libOpenflow@v0.0.0-20210609050114-d967b14cc688/protocol/dhcp.go (about)

     1  package protocol
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"io"
     8  	"math/rand"
     9  	"net"
    10  )
    11  
    12  const (
    13  	DHCP_MSG_BOOT_REQ byte = iota
    14  	DHCP_MSG_BOOT_RES
    15  )
    16  
    17  type DHCPOperation byte
    18  
    19  const (
    20  	DHCP_MSG_UNSPEC DHCPOperation = iota
    21  	DHCP_MSG_DISCOVER
    22  	DHCP_MSG_OFFER
    23  	DHCP_MSG_REQUEST
    24  	DHCP_MSG_DECLINE
    25  	DHCP_MSG_ACK
    26  	DHCP_MSG_NAK
    27  	DHCP_MSG_RELEASE
    28  	DHCP_MSG_INFORM
    29  )
    30  
    31  var dhcpMagic uint32 = 0x63825363
    32  
    33  type DHCP struct {
    34  	Operation    DHCPOperation
    35  	HardwareType byte
    36  	HardwareLen  uint8
    37  	HardwareOpts uint8
    38  	Xid          uint32
    39  	Secs         uint16
    40  	Flags        uint16
    41  	ClientIP     net.IP
    42  	YourIP       net.IP
    43  	ServerIP     net.IP
    44  	GatewayIP    net.IP
    45  	ClientHWAddr net.HardwareAddr
    46  	ServerName   [64]byte
    47  	File         [128]byte
    48  	Options      []DHCPOption
    49  }
    50  
    51  const (
    52  	DHCP_OPT_REQUEST_IP     byte = iota + 50 // 0x32, 4, net.IP
    53  	DHCP_OPT_LEASE_TIME                      // 0x33, 4, uint32
    54  	DHCP_OPT_EXT_OPTS                        // 0x34, 1, 1/2/3
    55  	DHCP_OPT_MESSAGE_TYPE                    // 0x35, 1, 1-7
    56  	DHCP_OPT_SERVER_ID                       // 0x36, 4, net.IP
    57  	DHCP_OPT_PARAMS_REQUEST                  // 0x37, n, []byte
    58  	DHCP_OPT_MESSAGE                         // 0x38, n, string
    59  	DHCP_OPT_MAX_DHCP_SIZE                   // 0x39, 2, uint16
    60  	DHCP_OPT_T1                              // 0x3a, 4, uint32
    61  	DHCP_OPT_T2                              // 0x3b, 4, uint32
    62  	DHCP_OPT_CLASS_ID                        // 0x3c, n, []byte
    63  	DHCP_OPT_CLIENT_ID                       // 0x3d, n >=  2, []byte
    64  
    65  )
    66  
    67  const (
    68  	DHCP_HW_ETHERNET byte = 0x01
    69  )
    70  
    71  const (
    72  	DHCP_FLAG_BROADCAST uint16 = 0x80
    73  
    74  //	FLAG_BROADCAST_MASK uint16 = (1 << FLAG_BROADCAST)
    75  )
    76  
    77  func NewDHCP(xid uint32, op DHCPOperation, hwtype byte) (*DHCP, error) {
    78  	if xid == 0 {
    79  		xid = rand.Uint32()
    80  	}
    81  	switch hwtype {
    82  	case DHCP_HW_ETHERNET:
    83  		break
    84  	default:
    85  		return nil, errors.New("Bad HardwareType")
    86  	}
    87  	d := &DHCP{
    88  		Operation:    op,
    89  		HardwareType: hwtype,
    90  		Xid:          xid,
    91  		ClientIP:     make([]byte, 4),
    92  		YourIP:       make([]byte, 4),
    93  		ServerIP:     make([]byte, 4),
    94  		GatewayIP:    make([]byte, 4),
    95  		ClientHWAddr: make([]byte, 16),
    96  	}
    97  	return d, nil
    98  }
    99  
   100  func (d *DHCP) Len() (n uint16) {
   101  	n += uint16(240)
   102  	optend := false
   103  	for _, opt := range d.Options {
   104  		n += opt.Len()
   105  		if opt.OptionType() == DHCP_OPT_END {
   106  			optend = true
   107  		}
   108  	}
   109  	if !optend {
   110  		n += 1
   111  	}
   112  	return
   113  }
   114  
   115  func (d *DHCP) Read(b []byte) (n int, err error) {
   116  	buf := new(bytes.Buffer)
   117  	binary.Write(buf, binary.BigEndian, d.Operation)
   118  	n += 1
   119  	binary.Write(buf, binary.BigEndian, d.HardwareType)
   120  	n += 1
   121  	binary.Write(buf, binary.BigEndian, d.HardwareLen)
   122  	n += 1
   123  	binary.Write(buf, binary.BigEndian, d.HardwareOpts)
   124  	n += 1
   125  	binary.Write(buf, binary.BigEndian, d.Xid)
   126  	n += 4
   127  	binary.Write(buf, binary.BigEndian, d.Secs)
   128  	n += 2
   129  	binary.Write(buf, binary.BigEndian, d.Flags)
   130  	n += 2
   131  	binary.Write(buf, binary.BigEndian, d.ClientIP)
   132  	n += 4
   133  	binary.Write(buf, binary.BigEndian, d.YourIP)
   134  	n += 4
   135  	binary.Write(buf, binary.BigEndian, d.ServerIP)
   136  	n += 4
   137  	binary.Write(buf, binary.BigEndian, d.GatewayIP)
   138  	n += 4
   139  	clientHWAddr := make([]byte, 16)
   140  	copy(clientHWAddr[0:], d.ClientHWAddr)
   141  	binary.Write(buf, binary.BigEndian, clientHWAddr)
   142  	n += 16
   143  	binary.Write(buf, binary.BigEndian, d.ServerName)
   144  	n += 64
   145  	binary.Write(buf, binary.BigEndian, d.File)
   146  	n += 128
   147  	binary.Write(buf, binary.BigEndian, dhcpMagic)
   148  	n += 4
   149  
   150  	optend := false
   151  	for _, opt := range d.Options {
   152  		m, err := DHCPWriteOption(buf, opt)
   153  		n += m
   154  		if err != nil {
   155  			return n, err
   156  		}
   157  		if opt.OptionType() == DHCP_OPT_END {
   158  			optend = true
   159  		}
   160  	}
   161  	if !optend {
   162  		m, err := DHCPWriteOption(buf, DHCPNewOption(DHCP_OPT_END, nil))
   163  		n += m
   164  		if err != nil {
   165  			return n, err
   166  		}
   167  	}
   168  	if n, err = buf.Read(b); n == 0 {
   169  		return
   170  	}
   171  	return n, nil
   172  }
   173  
   174  func (d *DHCP) Write(b []byte) (n int, err error) {
   175  	if len(b) < 240 {
   176  		return 0, errors.New("ErrTruncated")
   177  	}
   178  	buf := bytes.NewBuffer(b)
   179  
   180  	if err = binary.Read(buf, binary.BigEndian, &d.Operation); err != nil {
   181  		return
   182  	}
   183  	n += 1
   184  	if err = binary.Read(buf, binary.BigEndian, &d.HardwareType); err != nil {
   185  		return
   186  	}
   187  	n += 1
   188  	if err = binary.Read(buf, binary.BigEndian, &d.HardwareLen); err != nil {
   189  		return
   190  	}
   191  	n += 1
   192  	if err = binary.Read(buf, binary.BigEndian, &d.HardwareOpts); err != nil {
   193  		return
   194  	}
   195  	n += 1
   196  	if err = binary.Read(buf, binary.BigEndian, &d.Xid); err != nil {
   197  		return
   198  	}
   199  	n += 4
   200  	if err = binary.Read(buf, binary.BigEndian, &d.Secs); err != nil {
   201  		return
   202  	}
   203  	n += 2
   204  	if err = binary.Read(buf, binary.BigEndian, &d.Flags); err != nil {
   205  		return
   206  	}
   207  	n += 2
   208  	d.ClientIP = make([]byte, 4)
   209  	if err = binary.Read(buf, binary.BigEndian, &d.ClientIP); err != nil {
   210  		return
   211  	}
   212  	n += 4
   213  	d.YourIP = make([]byte, 4)
   214  	if err = binary.Read(buf, binary.BigEndian, &d.YourIP); err != nil {
   215  		return
   216  	}
   217  	n += 4
   218  	d.ServerIP = make([]byte, 4)
   219  	if err = binary.Read(buf, binary.BigEndian, &d.ServerIP); err != nil {
   220  		return
   221  	}
   222  	n += 4
   223  	d.GatewayIP = make([]byte, 4)
   224  	if err = binary.Read(buf, binary.BigEndian, &d.GatewayIP); err != nil {
   225  		return
   226  	}
   227  	n += 4
   228  	clientHWAddr := make([]byte, 16)
   229  	if err = binary.Read(buf, binary.BigEndian, &clientHWAddr); err != nil {
   230  		return
   231  	}
   232  	d.ClientHWAddr = net.HardwareAddr(clientHWAddr[:d.HardwareLen])
   233  	n += 16
   234  
   235  	if err = binary.Read(buf, binary.BigEndian, &d.ServerName); err != nil {
   236  		return
   237  	}
   238  	n += 64
   239  	if err = binary.Read(buf, binary.BigEndian, &d.File); err != nil {
   240  		return
   241  	}
   242  	n += 128
   243  
   244  	var magic uint32
   245  	if err = binary.Read(buf, binary.BigEndian, &magic); err != nil {
   246  		return
   247  	}
   248  	n += 4
   249  
   250  	if magic != dhcpMagic {
   251  		return n, errors.New("Bad DHCP header")
   252  	}
   253  
   254  	optlen := buf.Len()
   255  	opts := make([]byte, optlen)
   256  	if err = binary.Read(buf, binary.BigEndian, &opts); err != nil {
   257  		return
   258  	}
   259  	n += optlen
   260  
   261  	if d.Options, err = DHCPParseOptions(opts); err != nil {
   262  		return
   263  	}
   264  
   265  	return
   266  }
   267  
   268  // Standard options (RFC1533)
   269  const (
   270  	DHCP_OPT_PAD                      byte = iota
   271  	DHCP_OPT_SUBNET_MASK                   // 0x01, 4, net.IP
   272  	DHCP_OPT_TIME_OFFSET                   // 0x02, 4, int32 (signed seconds from UTC)
   273  	DHCP_OPT_DEFAULT_GATEWAY               // 0x03, n*4, [n]net.IP
   274  	DHCP_OPT_TIME_SERVER                   // 0x04, n*4, [n]net.IP
   275  	DHCP_OPT_NAME_SERVER                   // 0x05, n*4, [n]net.IP
   276  	DHCP_OPT_DOMAIN_NAME_SERVERS           // 0x06, n*4, [n]net.IP
   277  	DHCP_OPT_LOG_SERVER                    // 0x07, n*4, [n]net.IP
   278  	DHCP_OPT_COOKIE_SERVER                 // 0x08, n*4, [n]net.IP
   279  	DHCP_OPT_LPR_SERVER                    // 0x09, n*4, [n]net.IP
   280  	DHCP_OPT_IMPRESS_SERVER                // 0x0a, n*4, [n]net.IP
   281  	DHCP_OPT_RLSERVER                      // 0x0b, n*4, [n]net.IP
   282  	DHCP_OPT_HOST_NAME                     // 0x0c, n, string
   283  	DHCP_OPT_BOOTFILE_SIZE                 // 0x0d, 2, uint16
   284  	DHCP_OPT_MERIT_DUMP_FILE               // 0x0e, >1, string
   285  	DHCP_OPT_DOMAIN_NAME                   // 0x0f, n, string
   286  	DHCP_OPT_SWAP_SERVER                   // 0x10, n*4, [n]net.IP
   287  	DHCP_OPT_ROOT_PATH                     // 0x11, n, string
   288  	DHCP_OPT_EXTENSIONS_PATH               // 0x12, n, string
   289  	DHCP_OPT_IP_FORWARDING                 // 0x13, 1, bool
   290  	DHCP_OPT_SOURCE_ROUTING                // 0x14, 1, bool
   291  	DHCP_OPT_POLICY_FILTER                 // 0x15, 8*n, [n]{net.IP/net.IP}
   292  	DHCP_OPT_DGRAM_MTU                     // 0x16, 2, uint16
   293  	DHCP_OPT_DEFAULT_TTL                   // 0x17, 1, byte
   294  	DHCP_OPT_PATH_MTU_AGING_TIMEOUT        // 0x18, 4, uint32
   295  	DHCP_OPT_PATH_PLATEU_TABLE_OPTION      // 0x19, 2*n, []uint16
   296  	DHCP_OPT_INTERFACE_MTU                 //0x1a, 2, uint16
   297  	DHCP_OPT_ALL_SUBS_LOCAL                // 0x1b, 1, bool
   298  	DHCP_OPT_BROADCAST_ADDR                // 0x1c, 4, net.IP
   299  	DHCP_OPT_MASK_DISCOVERY                // 0x1d, 1, bool
   300  	DHCP_OPT_MASK_SUPPLIER                 // 0x1e, 1, bool
   301  	DHCP_OPT_ROUTER_DISCOVERY              // 0x1f, 1, bool
   302  	DHCP_OPT_ROUTER_SOLICIT_ADDR           // 0x20, 4, net.IP
   303  	DHCP_OPT_STATIC_ROUTE                  // 0x21, n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask
   304  	DHCP_OPT_ARP_TRAILERS                  // 0x22, 1, bool
   305  	DHCP_OPT_ARP_TIMEOUT                   // 0x23, 4, uint32
   306  	DHCP_OPT_ETHERNET_ENCAP                // 0x24, 1, bool
   307  	DHCP_OPT_TCP_TTL                       // 0x25,1, byte
   308  	DHCP_OPT_TCP_KEEPALIVE_INT             // 0x26,4, uint32
   309  	DHCP_OPT_TCP_KEEPALIVE_GARBAGE         // 0x27,1, bool
   310  	DHCP_OPT_NIS_DOMAIN                    // 0x28,n, string
   311  	DHCP_OPT_NIS_SERVERS                   // 0x29,4*n,  [n]net.IP
   312  	DHCP_OPT_NTP_SERVERS                   // 0x2a, 4*n, [n]net.IP
   313  	DHCP_OPT_VENDOR_OPT                    // 0x2b, n, [n]byte // may be encapsulated.
   314  	DHCP_OPT_NETBIOS_IPNS                  // 0x2c, 4*n, [n]net.IP
   315  	DHCP_OPT_NETBIOS_DDS                   // 0x2d, 4*n, [n]net.IP
   316  	DHCP_OPT_NETBIOS_NODE_TYPE             // 0x2e, 1, magic byte
   317  	DHCP_OPT_NETBIOS_SCOPE                 // 0x2f, n, string
   318  	DHCP_OPT_X_FONT_SERVER                 // 0x30, n, string
   319  	DHCP_OPT_X_DISPLAY_MANAGER             // 0x31, n, string
   320  
   321  	DHCP_OPT_SIP_SERVERS byte = 0x78 // 0x78!, n, url
   322  	DHCP_OPT_END         byte = 0xff
   323  )
   324  
   325  // I'm amazed that this is syntactically valid.
   326  // cool though.
   327  var DHCPOptionTypeStrings = [256]string{
   328  	DHCP_OPT_PAD:                      "(padding)",
   329  	DHCP_OPT_SUBNET_MASK:              "SubnetMask",
   330  	DHCP_OPT_TIME_OFFSET:              "TimeOffset",
   331  	DHCP_OPT_DEFAULT_GATEWAY:          "DefaultGateway",
   332  	DHCP_OPT_TIME_SERVER:              "rfc868", // old time server protocol, stringified to dissuade confusion w. NTP
   333  	DHCP_OPT_NAME_SERVER:              "ien116", // obscure nameserver protocol, stringified to dissuade confusion w. DNS
   334  	DHCP_OPT_DOMAIN_NAME_SERVERS:      "DNS",
   335  	DHCP_OPT_LOG_SERVER:               "mitLCS", // MIT LCS server protocol, yada yada w. Syslog
   336  	DHCP_OPT_COOKIE_SERVER:            "OPT_COOKIE_SERVER",
   337  	DHCP_OPT_LPR_SERVER:               "OPT_LPR_SERVER",
   338  	DHCP_OPT_IMPRESS_SERVER:           "OPT_IMPRESS_SERVER",
   339  	DHCP_OPT_RLSERVER:                 "OPT_RLSERVER",
   340  	DHCP_OPT_HOST_NAME:                "Hostname",
   341  	DHCP_OPT_BOOTFILE_SIZE:            "BootfileSize",
   342  	DHCP_OPT_MERIT_DUMP_FILE:          "OPT_MERIT_DUMP_FILE",
   343  	DHCP_OPT_DOMAIN_NAME:              "DomainName",
   344  	DHCP_OPT_SWAP_SERVER:              "OPT_SWAP_SERVER",
   345  	DHCP_OPT_ROOT_PATH:                "RootPath",
   346  	DHCP_OPT_EXTENSIONS_PATH:          "OPT_EXTENSIONS_PATH",
   347  	DHCP_OPT_IP_FORWARDING:            "OPT_IP_FORWARDING",
   348  	DHCP_OPT_SOURCE_ROUTING:           "OPT_SOURCE_ROUTING",
   349  	DHCP_OPT_POLICY_FILTER:            "OPT_POLICY_FILTER",
   350  	DHCP_OPT_DGRAM_MTU:                "OPT_DGRAM_MTU",
   351  	DHCP_OPT_DEFAULT_TTL:              "OPT_DEFAULT_TTL",
   352  	DHCP_OPT_PATH_MTU_AGING_TIMEOUT:   "OPT_PATH_MTU_AGING_TIMEOUT",
   353  	DHCP_OPT_PATH_PLATEU_TABLE_OPTION: "OPT_PATH_PLATEU_TABLE_OPTION",
   354  	DHCP_OPT_INTERFACE_MTU:            "OPT_INTERFACE_MTU",
   355  	DHCP_OPT_ALL_SUBS_LOCAL:           "OPT_ALL_SUBS_LOCAL",
   356  	DHCP_OPT_BROADCAST_ADDR:           "OPT_BROADCAST_ADDR",
   357  	DHCP_OPT_MASK_DISCOVERY:           "OPT_MASK_DISCOVERY",
   358  	DHCP_OPT_MASK_SUPPLIER:            "OPT_MASK_SUPPLIER",
   359  	DHCP_OPT_ROUTER_DISCOVERY:         "OPT_ROUTER_DISCOVERY",
   360  	DHCP_OPT_ROUTER_SOLICIT_ADDR:      "OPT_ROUTER_SOLICIT_ADDR",
   361  	DHCP_OPT_STATIC_ROUTE:             "OPT_STATIC_ROUTE",
   362  	DHCP_OPT_ARP_TRAILERS:             "OPT_ARP_TRAILERS",
   363  	DHCP_OPT_ARP_TIMEOUT:              "OPT_ARP_TIMEOUT",
   364  	DHCP_OPT_ETHERNET_ENCAP:           "OPT_ETHERNET_ENCAP",
   365  	DHCP_OPT_TCP_TTL:                  "OPT_TCP_TTL",
   366  	DHCP_OPT_TCP_KEEPALIVE_INT:        "OPT_TCP_KEEPALIVE_INT",
   367  	DHCP_OPT_TCP_KEEPALIVE_GARBAGE:    "OPT_TCP_KEEPALIVE_GARBAGE",
   368  	DHCP_OPT_NIS_DOMAIN:               "OPT_NIS_DOMAIN",
   369  	DHCP_OPT_NIS_SERVERS:              "OPT_NIS_SERVERS",
   370  	DHCP_OPT_NTP_SERVERS:              "OPT_NTP_SERVERS",
   371  	DHCP_OPT_VENDOR_OPT:               "OPT_VENDOR_OPT",
   372  	DHCP_OPT_NETBIOS_IPNS:             "OPT_NETBIOS_IPNS",
   373  	DHCP_OPT_NETBIOS_DDS:              "OPT_NETBIOS_DDS",
   374  	DHCP_OPT_NETBIOS_NODE_TYPE:        "OPT_NETBIOS_NODE_TYPE",
   375  	DHCP_OPT_NETBIOS_SCOPE:            "OPT_NETBIOS_SCOPE",
   376  	DHCP_OPT_X_FONT_SERVER:            "OPT_X_FONT_SERVER",
   377  	DHCP_OPT_X_DISPLAY_MANAGER:        "OPT_X_DISPLAY_MANAGER",
   378  	DHCP_OPT_END:                      "(end)",
   379  	DHCP_OPT_SIP_SERVERS:              "SipServers",
   380  	DHCP_OPT_REQUEST_IP:               "RequestIP",
   381  	DHCP_OPT_LEASE_TIME:               "LeaseTime",
   382  	DHCP_OPT_EXT_OPTS:                 "ExtOpts",
   383  	DHCP_OPT_MESSAGE_TYPE:             "MessageType",
   384  	DHCP_OPT_SERVER_ID:                "ServerID",
   385  	DHCP_OPT_PARAMS_REQUEST:           "ParamsRequest",
   386  	DHCP_OPT_MESSAGE:                  "Message",
   387  	DHCP_OPT_MAX_DHCP_SIZE:            "MaxDHCPSize",
   388  	DHCP_OPT_T1:                       "Timer1",
   389  	DHCP_OPT_T2:                       "Timer2",
   390  	DHCP_OPT_CLASS_ID:                 "ClassID",
   391  	DHCP_OPT_CLIENT_ID:                "ClientID",
   392  }
   393  
   394  type DHCPOption interface {
   395  	OptionType() byte
   396  	Bytes() []byte
   397  	Len() uint16
   398  }
   399  
   400  // Write an option to an io.Writer, including tag  & length
   401  // (if length is appropriate to the tag type).
   402  // Utilizes the MarshalOption as the underlying serializer.
   403  func DHCPWriteOption(w io.Writer, a DHCPOption) (n int, err error) {
   404  	out, err := DHCPMarshalOption(a)
   405  	if err == nil {
   406  		n, err = w.Write(out)
   407  	}
   408  	return
   409  }
   410  
   411  type dhcpoption struct {
   412  	tag  byte
   413  	data []byte
   414  }
   415  
   416  // A more json.Marshal like version of WriteOption.
   417  func DHCPMarshalOption(o DHCPOption) (out []byte, err error) {
   418  	switch o.OptionType() {
   419  	case DHCP_OPT_PAD, DHCP_OPT_END:
   420  		out = []byte{o.OptionType()}
   421  	default:
   422  		dlen := len(o.Bytes())
   423  		if dlen > 253 {
   424  			err = errors.New("Data too long to marshal")
   425  		} else {
   426  			out = make([]byte, dlen+2)
   427  			out[0], out[1] = o.OptionType(), byte(dlen)
   428  			copy(out[2:], o.Bytes())
   429  		}
   430  	}
   431  	return
   432  }
   433  
   434  func (self dhcpoption) Len() uint16      { return uint16(len(self.data) + 2) }
   435  func (self dhcpoption) Bytes() []byte    { return self.data }
   436  func (self dhcpoption) OptionType() byte { return self.tag }
   437  
   438  func DHCPNewOption(tag byte, data []byte) DHCPOption {
   439  	return &dhcpoption{tag: tag, data: data}
   440  }
   441  
   442  // NB: We don't validate that you have /any/ IP's in the option here,
   443  // simply that if you do that they're valid. Most DHCP options are only
   444  // valid with 1(+|) values
   445  func DHCPIP4sOption(tag byte, ips []net.IP) (opt DHCPOption, err error) {
   446  	var out []byte = make([]byte, 4*len(ips))
   447  	for i := range ips {
   448  		ip := ips[i].To4()
   449  		if ip == nil {
   450  			err = errors.New("ip is not a valid IPv4 address")
   451  		} else {
   452  			copy(out[i*4:], []byte(ip))
   453  		}
   454  		if err != nil {
   455  			break
   456  		}
   457  	}
   458  	opt = DHCPNewOption(tag, out)
   459  	return
   460  }
   461  
   462  // NB: We don't validate that you have /any/ IP's in the option here,
   463  // simply that if you do that they're valid. Most DHCP options are only
   464  // valid with 1(+|) values
   465  func DHCPIP4Option(tag byte, ips net.IP) (opt DHCPOption, err error) {
   466  	ips = ips.To4()
   467  	if ips == nil {
   468  		err = errors.New("ip is not a valid IPv4 address")
   469  		return
   470  	}
   471  	opt = DHCPNewOption(tag, []byte(ips))
   472  	return
   473  }
   474  
   475  // NB: I'm not checking tag : min length here!
   476  func DHCPStringOption(tag byte, s string) (opt DHCPOption, err error) {
   477  	opt = &dhcpoption{tag: tag, data: bytes.NewBufferString(s).Bytes()}
   478  	return
   479  }
   480  
   481  func DHCPParseOptions(in []byte) (opts []DHCPOption, err error) {
   482  	pos := 0
   483  	for pos < len(in) && err == nil {
   484  		var tag = in[pos]
   485  		pos++
   486  		switch tag {
   487  		case DHCP_OPT_PAD:
   488  			opts = append(opts, DHCPNewOption(tag, []byte{}))
   489  		case DHCP_OPT_END:
   490  			return
   491  		default:
   492  			if len(in)-pos >= 1 {
   493  				_len := in[pos]
   494  				pos++
   495  				opts = append(opts, DHCPNewOption(tag, in[pos:pos+int(_len)]))
   496  				pos += int(_len)
   497  			}
   498  		}
   499  	}
   500  	return
   501  }
   502  
   503  func NewDHCPDiscover(xid uint32, hwAddr net.HardwareAddr) (d *DHCP, err error) {
   504  	if d, err = NewDHCP(xid, DHCP_MSG_DISCOVER, DHCP_HW_ETHERNET); err != nil {
   505  		return
   506  	}
   507  	d.HardwareLen = uint8(len(hwAddr))
   508  	d.ClientHWAddr = hwAddr
   509  	d.Options = append(d.Options, DHCPNewOption(53, []byte{byte(DHCP_MSG_DISCOVER)}))
   510  	d.Options = append(d.Options, DHCPNewOption(DHCP_OPT_CLIENT_ID, hwAddr))
   511  	return
   512  }
   513  
   514  func NewDHCPOffer(xid uint32, hwAddr net.HardwareAddr) (d *DHCP, err error) {
   515  	if d, err = NewDHCP(xid, DHCP_MSG_OFFER, DHCP_HW_ETHERNET); err != nil {
   516  		return
   517  	}
   518  	d.HardwareLen = uint8(len(hwAddr))
   519  	d.ClientHWAddr = hwAddr
   520  	d.Options = append(d.Options, DHCPNewOption(53, []byte{byte(DHCP_MSG_OFFER)}))
   521  	return
   522  }
   523  
   524  func NewDHCPRequest(xid uint32, hwAddr net.HardwareAddr) (d *DHCP, err error) {
   525  	if d, err = NewDHCP(xid, DHCP_MSG_REQUEST, DHCP_HW_ETHERNET); err != nil {
   526  		return
   527  	}
   528  	d.HardwareLen = uint8(len(hwAddr))
   529  	d.ClientHWAddr = hwAddr
   530  	d.Options = append(d.Options, DHCPNewOption(53, []byte{byte(DHCP_MSG_REQUEST)}))
   531  	return
   532  }
   533  
   534  func NewDHCPAck(xid uint32, hwAddr net.HardwareAddr) (d *DHCP, err error) {
   535  	if d, err = NewDHCP(xid, DHCP_MSG_ACK, DHCP_HW_ETHERNET); err != nil {
   536  		return
   537  	}
   538  	d.HardwareLen = uint8(len(hwAddr))
   539  	d.ClientHWAddr = hwAddr
   540  	d.Options = append(d.Options, DHCPNewOption(53, []byte{byte(DHCP_MSG_ACK)}))
   541  	return
   542  }
   543  
   544  func NewDHCPNak(xid uint32, hwAddr net.HardwareAddr) (d *DHCP, err error) {
   545  	if d, err = NewDHCP(xid, DHCP_MSG_NAK, DHCP_HW_ETHERNET); err != nil {
   546  		return
   547  	}
   548  	d.HardwareLen = uint8(len(hwAddr))
   549  	d.ClientHWAddr = hwAddr
   550  	d.Options = append(d.Options, DHCPNewOption(53, []byte{byte(DHCP_MSG_NAK)}))
   551  	return
   552  }