ONOS-2926 Sending DHCPNAKs to failed ASSIGN request

Change-Id: I04658313c516934cd946733d8e30093523f83d7e
diff --git a/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
index 153463a..96d94a2 100644
--- a/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
+++ b/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -154,6 +154,8 @@
 
     private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
 
+    private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
+
     protected Timeout timeout;
 
     protected static int timerDelay = 2;
@@ -290,12 +292,18 @@
             DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
             DHCP dhcpReply = new DHCP();
             dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
-
-            dhcpReply.setYourIPAddress(ipOffered.toInt());
-            dhcpReply.setServerIPAddress(myIP.toInt());
-
-            dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
+            dhcpReply.setFlags(dhcpPacket.getFlags());
+            dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
             dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
+            dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
+
+            if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
+                dhcpReply.setYourIPAddress(ipOffered.toInt());
+                dhcpReply.setServerIPAddress(myIP.toInt());
+                if (dhcpPacket.getGatewayIPAddress() == 0) {
+                    ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
+                }
+            }
             dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
             dhcpReply.setHardwareAddressLength((byte) 6);
 
@@ -317,54 +325,57 @@
             option.setData(myIP.toOctets());
             optionList.add(option);
 
-            // IP Address Lease Time.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
-            option.setLength((byte) 4);
-            option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
-            optionList.add(option);
+            if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
 
-            // IP Address Renewal Time.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
-            option.setLength((byte) 4);
-            option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
-            optionList.add(option);
+                // IP Address Lease Time.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
+                option.setLength((byte) 4);
+                option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
+                optionList.add(option);
 
-            // IP Address Rebinding Time.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
-            option.setLength((byte) 4);
-            option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
-            optionList.add(option);
+                // IP Address Renewal Time.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
+                option.setLength((byte) 4);
+                option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
+                optionList.add(option);
 
-            // Subnet Mask.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
-            option.setLength((byte) 4);
-            option.setData(subnetMask.toOctets());
-            optionList.add(option);
+                // IP Address Rebinding Time.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
+                option.setLength((byte) 4);
+                option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
+                optionList.add(option);
 
-            // Broadcast Address.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
-            option.setLength((byte) 4);
-            option.setData(broadcastAddress.toOctets());
-            optionList.add(option);
+                // Subnet Mask.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
+                option.setLength((byte) 4);
+                option.setData(subnetMask.toOctets());
+                optionList.add(option);
 
-            // Router Address.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
-            option.setLength((byte) 4);
-            option.setData(routerAddress.toOctets());
-            optionList.add(option);
+                // Broadcast Address.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
+                option.setLength((byte) 4);
+                option.setData(broadcastAddress.toOctets());
+                optionList.add(option);
 
-            // DNS Server Address.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
-            option.setLength((byte) 4);
-            option.setData(domainServer.toOctets());
-            optionList.add(option);
+                // Router Address.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
+                option.setLength((byte) 4);
+                option.setData(routerAddress.toOctets());
+                optionList.add(option);
+
+                // DNS Server Address.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
+                option.setLength((byte) 4);
+                option.setData(domainServer.toOctets());
+                optionList.add(option);
+            }
 
             // End Option.
             option = new DHCPOption();
@@ -447,37 +458,44 @@
 
                 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) {
 
-                    outgoingPacketType = DHCPPacketType.DHCPACK;
-
                     if (flagIfServerIP && flagIfRequestedIP) {
                         // SELECTING state
-                        if (myIP.equals(serverIP) &&
-                                dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
+                        if (myIP.equals(serverIP)) {
 
-                            Ethernet ethReply = buildReply(packet, requestedIP,
-                                    (byte) outgoingPacketType.getValue());
+                            if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
+                                outgoingPacketType = DHCPPacketType.DHCPACK;
+                                discoverHost(context, requestedIP);
+                            } else {
+                                outgoingPacketType = DHCPPacketType.DHCPNAK;
+                            }
+                            Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
                             sendReply(context, ethReply);
-                            discoverHost(context, requestedIP);
                         }
                     } else if (flagIfRequestedIP) {
                         // INIT-REBOOT state
                         if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
-                            Ethernet ethReply = buildReply(packet, requestedIP,
-                                    (byte) outgoingPacketType.getValue());
+                            outgoingPacketType = DHCPPacketType.DHCPACK;
+                            Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
                             sendReply(context, ethReply);
                             discoverHost(context, requestedIP);
                         }
+
                     } else {
                         // RENEWING and REBINDING state
                         int ciaadr = dhcpPayload.getClientIPAddress();
                         if (ciaadr != 0) {
                             Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
                             if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime)) {
-                                Ethernet ethReply = buildReply(packet, clientIaddr,
-                                        (byte) outgoingPacketType.getValue());
-                                sendReply(context, ethReply);
+                                outgoingPacketType = DHCPPacketType.DHCPACK;
                                 discoverHost(context, clientIaddr);
+                            } else if (packet.getEtherType() == Ethernet.TYPE_IPV4 &&
+                                    ((IPv4) packet.getPayload()).getDestinationAddress() == myIP.toInt()) {
+                                outgoingPacketType = DHCPPacketType.DHCPNAK;
+                            } else {
+                                return;
                             }
+                            Ethernet ethReply = buildReply(packet, clientIaddr, (byte) outgoingPacketType.getValue());
+                            sendReply(context, ethReply);
                         }
                     }
                 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) {