dhcp: send client-id option and report NAKs
Most of the clients I have watched with tcpdump send the Client-ID
field (even though they only populate it with the MAC address which
is already sent in chaddr), so for consistency let's send it too.
Change-Id: I096908d6bc2d40ddfdad9a0f749fcc1868552c02
diff --git a/dhcp/client.go b/dhcp/client.go
index 4ba3fff..e4d6de2 100644
--- a/dhcp/client.go
+++ b/dhcp/client.go
@@ -149,6 +149,13 @@
if requestedAddr != "" {
options = append(options, option{optReqIPAddr, []byte(requestedAddr)})
}
+ var clientID []byte
+ if len(c.linkAddr) == 6 {
+ clientID = make([]byte, 7)
+ clientID[0] = 1 // htype: ARP Ethernet from RFC 1700
+ copy(clientID[1:], c.linkAddr)
+ options = append(options, option{optClientID, clientID})
+ }
h := make(header, headerBaseSize+options.len())
h.init()
h.setOp(opRequest)
@@ -229,11 +236,15 @@
for i, b := 0, h.yiaddr(); i < len(b); i++ {
b[i] = 0
}
- h.setOptions([]option{
+ options = []option{
{optDHCPMsgType, []byte{byte(dhcpREQUEST)}},
{optReqIPAddr, []byte(addr)},
{optDHCPServer, []byte(cfg.ServerAddress)},
- })
+ }
+ if len(clientID) != 0 {
+ options = append(options, option{optClientID, clientID})
+ }
+ h.setOptions(options)
if _, err := ep.Write([]byte(h), serverAddr); err != nil {
return fmt.Errorf("dhcp discovery write: %v", err)
}
@@ -266,6 +277,12 @@
if err != nil {
return fmt.Errorf("dhcp ack: %v", err)
}
+ if msgtype == dhcpNAK {
+ if msg := opts.message(); msg != "" {
+ return fmt.Errorf("dhcp: NAK %q", msg)
+ }
+ return fmt.Errorf("dhcp: NAK with no message")
+ }
ack = msgtype == dhcpACK
if !ack {
return fmt.Errorf("dhcp: request not acknowledged")
diff --git a/dhcp/dhcp.go b/dhcp/dhcp.go
index 63d0184..0633236 100644
--- a/dhcp/dhcp.go
+++ b/dhcp/dhcp.go
@@ -179,6 +179,8 @@
optDHCPMsgType optionCode = 53 // dhcpMsgType
optDHCPServer optionCode = 54
optParamReq optionCode = 55
+ optMessage optionCode = 56
+ optClientID optionCode = 61
)
func (code optionCode) lenValid(l int) bool {
@@ -190,6 +192,8 @@
return l == 1
case optDomainNameServer:
return l%4 == 0
+ case optMessage, optClientID:
+ return l >= 1
case optParamReq:
return true // no fixed length
default:
@@ -215,6 +219,10 @@
return "option(server)"
case optParamReq:
return "option(parameter-request)"
+ case optMessage:
+ return "option(message)"
+ case optClientID:
+ return "option(client-id)"
default:
return fmt.Sprintf("option(%d)", code)
}
@@ -238,6 +246,15 @@
return 0, nil
}
+func (opts options) message() string {
+ for _, opt := range opts {
+ if opt.code == optMessage {
+ return string(opt.body)
+ }
+ }
+ return ""
+}
+
func (opts options) len() int {
l := 0
for _, opt := range opts {