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 }