Make some DHCP options and host discovery configurable

- Added broadcast option
- Made host discovery from DHCP configurable
- Some code cleanups

Change-Id: I42191c2fd17ef309c73a5382730d708686b835cd
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/impl/CordVtnInstanceManager.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/impl/CordVtnInstanceManager.java
index 4b85c27..3ca7361 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/impl/CordVtnInstanceManager.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/impl/CordVtnInstanceManager.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.cordvtn.impl;
 
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -34,6 +33,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.dhcp.DhcpService;
+import org.onosproject.dhcp.IpAssignment;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultAnnotations;
@@ -70,7 +70,7 @@
 import org.onosproject.xosclient.api.XosClientService;
 import org.slf4j.Logger;
 
-import java.util.List;
+import java.util.Date;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -82,6 +82,8 @@
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.cordvtn.api.Instance.*;
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
+import static org.onosproject.xosclient.api.VtnService.NetworkType.MANAGEMENT;
 import static org.onosproject.xosclient.api.VtnService.NetworkType.PRIVATE;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -97,6 +99,7 @@
     private static final String XOS_ACCESS_ERROR = "XOS access is not configured";
     private static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
     private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
+    private static final int DHCP_INFINITE_LEASE = -1;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
@@ -273,7 +276,6 @@
             arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
             arpProxy.sendGratuitousArpForGateway(service.serviceIp(), Sets.newHashSet(instance));
         }
-
         if (!instance.isNestedInstance()) {
             registerDhcpLease(instance, service);
         }
@@ -295,17 +297,25 @@
     }
 
     private void registerDhcpLease(Instance instance, VtnService service) {
-        List<Ip4Address> options = Lists.newArrayList();
-        options.add(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()));
-        options.add(service.serviceIp().getIp4Address());
-        options.add(service.serviceIp().getIp4Address());
-        options.add(DEFAULT_DNS);
+        Ip4Address broadcast = Ip4Address.makeMaskedAddress(
+                instance.ipAddress(),
+                service.subnet().prefixLength());
+
+        IpAssignment.Builder ipBuilder = IpAssignment.builder()
+                .ipAddress(instance.ipAddress())
+                .leasePeriod(DHCP_INFINITE_LEASE)
+                .timestamp(new Date())
+                .subnetMask(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()))
+                .broadcast(broadcast)
+                .domainServer(DEFAULT_DNS)
+                .assignmentStatus(Option_RangeNotEnforced);
+
+        if (service.networkType() != MANAGEMENT) {
+            ipBuilder = ipBuilder.routerAddress(service.serviceIp().getIp4Address());
+        }
 
         log.debug("Set static DHCP mapping for {} {}", instance.mac(), instance.ipAddress());
-        dhcpService.setStaticMapping(instance.mac(),
-                                     instance.ipAddress(),
-                                     true,
-                                     options);
+        dhcpService.setStaticMapping(instance.mac(), ipBuilder.build());
     }
 
     private VtnService getVtnService(VtnServiceId serviceId) {
diff --git a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpService.java b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpService.java
index 84eab6e..b200605 100644
--- a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpService.java
+++ b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpService.java
@@ -19,7 +19,6 @@
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.HostId;
 
-import java.util.List;
 import java.util.Map;
 
 
@@ -58,16 +57,12 @@
 
     /**
      * Registers a static IP mapping with the DHCP Server.
-     * Supports rangeNotEnforced option
      *
-     * @param macID macID of the client
-     * @param ipAddress IP Address requested for the client
-     * @param rangeNotEnforced true if rangeNotEnforced was set and the mapping will be eternal
-     * @param addressList subnetMask, DHCP/Router/DNS IP Addresses if rangeNotEnforced was set
+     * @param macAddress mac address to have a given ip assignment
+     * @param ipRequest ip address and dhcp options
      * @return true if the mapping was successfully added, false otherwise
      */
-    boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress, boolean rangeNotEnforced,
-                             List<Ip4Address> addressList);
+    boolean setStaticMapping(MacAddress macAddress, IpAssignment ipRequest);
 
     /**
      * Removes a static IP mapping with the DHCP Server.
diff --git a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpStore.java b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpStore.java
index c4be7ed..0b95e5a 100644
--- a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpStore.java
+++ b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/DhcpStore.java
@@ -19,7 +19,6 @@
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.HostId;
 
-import java.util.List;
 import java.util.Map;
 
 
@@ -41,7 +40,7 @@
      *
      * @param hostId Host ID of the client requesting an IP
      * @param requestedIP requested IP address
-     * @return IP address assigned to the Mac ID
+     * @return IP address assigned to the Mac address; null if no available IP
      */
     Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP);
 
@@ -50,14 +49,10 @@
      * Assigns the requested IP to the Mac ID, in response to a DHCP REQUEST message.
      *
      * @param hostId Host Id of the client requesting an IP
-     * @param ipAddr IP Address being requested
-     * @param leaseTime Lease time offered by the server for this mapping
-     * @param rangeNotEnforced true if rangeNotEnforced was set
-     * @param addressList subnetMask, DHCP/Router/DNS IP Addresses if rangeNotEnforced was set
+     * @param ipAssignment ip assignment
      * @return returns true if the assignment was successful, false otherwise
      */
-    boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean rangeNotEnforced,
-                     List<Ip4Address> addressList);
+    boolean assignIP(HostId hostId, IpAssignment ipAssignment);
 
 
     /**
@@ -92,21 +87,19 @@
     /**
      * Assigns the requested IP to the MAC ID (if available) for an indefinite period of time.
      *
-     * @param macID macID of the client
-     * @param ipAddr IP Address requested for the client
-     * @param rangeNotEnforced true if rangeNotEnforced was set
-     * @param addressList subnetMask, DHCP/Router/DNS IP Addresses rangeNotEnforced was set
+     * @param macAddress mac address of the client
+     * @param ipAssignment ip address and dhcp options requested for the client
      * @return true if the mapping was successfully registered, false otherwise
      */
-    boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean rangeNotEnforced, List<Ip4Address> addressList);
+    boolean assignStaticIP(MacAddress macAddress, IpAssignment ipAssignment);
 
     /**
      * Removes a static IP mapping associated with the given MAC ID from the DHCP Server.
      *
-     * @param macID macID of the client
+     * @param macAddress mac address of the client
      * @return true if the mapping was successfully registered, false otherwise
      */
-    boolean removeStaticIP(MacAddress macID);
+    boolean removeStaticIP(MacAddress macAddress);
 
     /**
      * Returns the list of all the available IPs with the server.
diff --git a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java
index e9c286c..bbf0c00 100644
--- a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java
+++ b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java
@@ -27,22 +27,15 @@
  */
 public final class IpAssignment {
 
+    // TODO make some dhcp options optional
     private final Ip4Address ipAddress;
-
     private final Date timestamp;
-
     private final long leasePeriod;
-
     private final Ip4Address subnetMask;
-
+    private final Ip4Address broadcast;
     private final Ip4Address dhcpServer;
-
     private final Ip4Address routerAddress;
-
     private final Ip4Address domainServer;
-
-    private final boolean rangeNotEnforced;
-
     private final AssignmentStatus assignmentStatus;
 
     public enum AssignmentStatus {
@@ -52,8 +45,10 @@
         Option_Requested,
 
         /**
-         * IP Assignment has been requested by a OpenStack.
+         * Static IP Assignment with unregistered IP range.
+         * This assignment can only be added or removed by set or remove static mapping.
          */
+        // TODO allow multiple IP ranges and remove this option
         Option_RangeNotEnforced,
         /**
          * IP has been assigned to a host.
@@ -70,30 +65,34 @@
      * Constructor for IPAssignment, where the ipAddress, the lease period, the timestamp
      * and assignment status is supplied.
      *
-     * @param ipAddress
-     * @param leasePeriod
-     * @param timestamp
-     * @param assignmentStatus
-     * @param subnetMask
-     * @param dhcpServer
-     * @param routerAddress
-     * @param domainServer
-     * @param rangeNotEnforced
+     * @param ipAddress ip address to assign
+     * @param leasePeriod lease period
+     * @param timestamp time stamp of the assignment
+     * @param assignmentStatus statue of the assignment
+     * @param subnetMask subnet mask of assigned ip range
+     * @param broadcast broadcast address
+     * @param dhcpServer dhcp server address
+     * @param routerAddress router address
+     * @param domainServer domain server address
      */
     private IpAssignment(Ip4Address ipAddress,
                          long leasePeriod,
                          Date timestamp,
-                         AssignmentStatus assignmentStatus, Ip4Address subnetMask, Ip4Address dhcpServer,
-                         Ip4Address routerAddress, Ip4Address domainServer, boolean rangeNotEnforced) {
+                         AssignmentStatus assignmentStatus,
+                         Ip4Address subnetMask,
+                         Ip4Address broadcast,
+                         Ip4Address dhcpServer,
+                         Ip4Address routerAddress,
+                         Ip4Address domainServer) {
         this.ipAddress = ipAddress;
         this.leasePeriod = leasePeriod;
         this.timestamp = timestamp;
         this.assignmentStatus = assignmentStatus;
         this.subnetMask = subnetMask;
+        this.broadcast = broadcast;
         this.dhcpServer = dhcpServer;
         this.routerAddress = routerAddress;
         this.domainServer = domainServer;
-        this.rangeNotEnforced = rangeNotEnforced;
     }
 
     /**
@@ -141,26 +140,51 @@
         return (int) this.leasePeriod * 1000;
     }
 
+    /**
+     * Returns subnet mask of the IP assignment.
+     *
+     * @return subnet mask
+     */
     public Ip4Address subnetMask() {
         return subnetMask;
     }
 
+    /**
+     * Returns broadcast address of the IP assignment.
+     *
+     * @return broadcast address
+     */
+    public Ip4Address broadcast() {
+        return broadcast;
+    }
+
+    /**
+     * Returns dhcp server of the IP assignment.
+     *
+     * @return dhcp server ip address
+     */
     public Ip4Address dhcpServer() {
         return dhcpServer;
     }
 
+    /**
+     * Returns router address of the IP assignment.
+     *
+     * @return router ip address
+     */
     public Ip4Address routerAddress() {
         return routerAddress;
     }
 
+    /**
+     * Returns domain server address.
+     *
+     * @return domain server ip address
+     */
     public Ip4Address domainServer() {
         return domainServer;
     }
 
-    public boolean rangeNotEnforced() {
-        return rangeNotEnforced;
-    }
-
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
@@ -169,10 +193,10 @@
                 .add("lease", leasePeriod)
                 .add("assignmentStatus", assignmentStatus)
                 .add("subnetMask", subnetMask)
+                .add("broadcast", broadcast)
                 .add("dhcpServer", dhcpServer)
                 .add("routerAddress", routerAddress)
                 .add("domainServer", domainServer)
-                .add("rangeNotEnforced", rangeNotEnforced)
                 .toString();
     }
 
@@ -201,25 +225,16 @@
     public static final class Builder {
 
         private Ip4Address ipAddress;
-
         private Date timeStamp;
-
         private long leasePeriod;
-
         private AssignmentStatus assignmentStatus;
-
         private Ip4Address subnetMask;
-
+        private Ip4Address broadcast;
         private Ip4Address dhcpServer;
-
+        private Ip4Address routerAddress;
         private Ip4Address domainServer;
 
-        private Ip4Address routerAddress;
-
-        private boolean rangeNotEnforced = false;
-
         private Builder() {
-
         }
 
         private Builder(IpAssignment ipAssignment) {
@@ -227,12 +242,24 @@
             timeStamp = ipAssignment.timestamp();
             leasePeriod = ipAssignment.leasePeriod();
             assignmentStatus = ipAssignment.assignmentStatus();
+            subnetMask = ipAssignment.subnetMask();
+            broadcast = ipAssignment.broadcast();
+            dhcpServer = ipAssignment.dhcpServer();
+            routerAddress = ipAssignment.routerAddress();
+            domainServer = ipAssignment.domainServer();
         }
 
         public IpAssignment build() {
             validateInputs();
-            return new IpAssignment(ipAddress, leasePeriod, timeStamp, assignmentStatus, subnetMask,
-                    dhcpServer, routerAddress, domainServer, rangeNotEnforced);
+            return new IpAssignment(ipAddress,
+                                    leasePeriod,
+                                    timeStamp,
+                                    assignmentStatus,
+                                    subnetMask,
+                                    broadcast,
+                                    dhcpServer,
+                                    routerAddress,
+                                    domainServer);
         }
 
         public Builder ipAddress(Ip4Address addr) {
@@ -260,6 +287,11 @@
             return this;
         }
 
+        public Builder broadcast(Ip4Address broadcast) {
+            this.broadcast = broadcast;
+            return this;
+        }
+
         public Builder dhcpServer(Ip4Address dhcpServer) {
             this.dhcpServer = dhcpServer;
             return this;
@@ -275,25 +307,12 @@
             return this;
         }
 
-        public Builder rangeNotEnforced(boolean rangeNotEnforced) {
-            this.rangeNotEnforced = rangeNotEnforced;
-            return this;
-        }
-
-
         private void validateInputs() {
             checkNotNull(ipAddress, "IP Address must be specified");
             checkNotNull(assignmentStatus, "Assignment Status must be specified");
             checkNotNull(leasePeriod, "Lease Period must be specified");
             checkNotNull(timeStamp, "Timestamp must be specified");
 
-            if (rangeNotEnforced) {
-                checkNotNull(subnetMask, "subnetMask must be specified in case of rangeNotEnforced");
-                checkNotNull(dhcpServer, "dhcpServer must be specified in case of rangeNotEnforced");
-                checkNotNull(domainServer, "domainServer must be specified in case of rangeNotEnforced");
-                checkNotNull(routerAddress, "routerAddress must be specified in case of rangeNotEnforced");
-            }
-
             switch (assignmentStatus) {
                 case Option_Requested:
                 case Option_RangeNotEnforced:
diff --git a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java
index 063a32b..eaf3a00 100644
--- a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java
+++ b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/cli/DhcpSetStaticMapping.java
@@ -15,13 +15,17 @@
  */
 package org.onosproject.dhcp.cli;
 
-import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.dhcp.DhcpService;
+import org.onosproject.dhcp.IpAssignment;
+
+import java.util.Date;
+
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Requested;
 
 /**
  * Registers a static MAC Address to IP Mapping with the DHCP Server.
@@ -49,7 +53,15 @@
         try {
             MacAddress macID = MacAddress.valueOf(macAddr);
             Ip4Address ipAddress = Ip4Address.valueOf(ipAddr);
-            if (dhcpService.setStaticMapping(macID, ipAddress, false, Lists.newArrayList())) {
+
+            IpAssignment ipAssignment = IpAssignment.builder()
+                    .ipAddress(ipAddress)
+                    .leasePeriod(dhcpService.getLeaseTime())
+                    .timestamp(new Date())
+                    .assignmentStatus(Option_Requested)
+                    .build();
+
+            if (dhcpService.setStaticMapping(macID, ipAssignment)) {
                 print(DHCP_SUCCESS);
             } else {
                 print(DHCP_FAILURE);
diff --git a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
index e35f71f..c7453ac 100644
--- a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
+++ b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
@@ -15,11 +15,13 @@
  */
 package org.onosproject.dhcp.impl;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
@@ -38,6 +40,8 @@
 import org.onlab.packet.UDP;
 import org.onlab.packet.VlanId;
 import org.onlab.util.Timer;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.dhcp.DhcpService;
@@ -66,19 +70,31 @@
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.Dictionary;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+
+import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp;
+import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
+import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RequestedIP;
+import static org.onlab.packet.DHCPPacketType.DHCPACK;
+import static org.onlab.packet.DHCPPacketType.DHCPNAK;
+import static org.onlab.packet.DHCPPacketType.DHCPOFFER;
 import static org.onlab.packet.MacAddress.valueOf;
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Requested;
 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
 
 /**
@@ -89,6 +105,9 @@
 public class DhcpManager implements DhcpService {
 
     private static final ProviderId PID = new ProviderId("of", "org.onosproject.dhcp", true);
+    private static final String ALLOW_HOST_DISCOVERY = "allowHostDiscovery";
+    private static final boolean DEFAULT_ALLOW_HOST_DISCOVERY = false;
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private final InternalConfigListener cfgListener = new InternalConfigListener();
@@ -120,44 +139,36 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostProviderRegistry hostProviderRegistry;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService componentConfigService;
+
+    @Property(name = ALLOW_HOST_DISCOVERY, boolValue = DEFAULT_ALLOW_HOST_DISCOVERY,
+            label = "Allow host discovery from DHCP request")
+    private boolean allowHostDiscovery = DEFAULT_ALLOW_HOST_DISCOVERY;
+
     protected HostProviderService hostProviderService;
-
     private final HostProvider hostProvider = new InternalHostProvider();
-
     private ApplicationId appId;
 
     // Hardcoded values are default values.
-
-    private static Ip4Address myIP = Ip4Address.valueOf("10.0.0.2");
-
-    private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
-
     /**
      * leaseTime - 10 mins or 600s.
      * renewalTime - 5 mins or 300s.
      * rebindingTime - 6 mins or 360s.
      */
-
     private static int leaseTime = 600;
-
     private static int renewalTime = 300;
-
     private static int rebindingTime = 360;
-
     private static byte packetTTL = (byte) 127;
-
     private static Ip4Address subnetMask = Ip4Address.valueOf("255.0.0.0");
-
     private static Ip4Address broadcastAddress = Ip4Address.valueOf("10.255.255.255");
-
     private static Ip4Address routerAddress = Ip4Address.valueOf("10.0.0.2");
-
     private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
-
+    private static Ip4Address myIP = Ip4Address.valueOf("10.0.0.2");
+    private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
     private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
 
     protected Timeout timeout;
-
     protected static int timerDelay = 2;
 
     @Activate
@@ -165,6 +176,7 @@
         // start the dhcp server
         appId = coreService.registerApplication("org.onosproject.dhcp");
 
+        componentConfigService.registerProperties(getClass());
         cfgService.addListener(cfgListener);
         factories.forEach(cfgService::registerConfigFactory);
         cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class));
@@ -187,6 +199,19 @@
         log.info("Stopped");
     }
 
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        String updatedConfig = Tools.get(properties, ALLOW_HOST_DISCOVERY);
+        if (!Strings.isNullOrEmpty(updatedConfig)) {
+            allowHostDiscovery = Boolean.valueOf(updatedConfig);
+            log.info("Host discovery is set to {}", updatedConfig);
+        }
+
+        log.info("Modified");
+    }
+
     /**
      * Request packet in via PacketService.
      */
@@ -241,12 +266,10 @@
     }
 
     @Override
-    public boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress, boolean rangeNotEnforced,
-                                    List<Ip4Address> addressList) {
-        log.debug("setStaticMapping is called with Mac: {}, Ip: {} addressList: {}",
-                macID.toString(), ipAddress.toString(), addressList.toString());
-
-        return dhcpStore.assignStaticIP(macID, ipAddress, rangeNotEnforced, addressList);
+    public boolean setStaticMapping(MacAddress macAddress, IpAssignment ipAssignment) {
+        log.debug("setStaticMapping is called with Mac: {} IpAssignment: {}",
+                  macAddress, ipAssignment);
+        return dhcpStore.assignStaticIP(macAddress, ipAssignment);
     }
 
     @Override
@@ -271,24 +294,25 @@
          */
         private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
 
-            Ip4Address subnetMaskReply;
-            Ip4Address dhcpServerReply;
-            Ip4Address routerAddressReply;
-            Ip4Address domainServerReply;
-            IpAssignment ipAssignment;
+            // mandatory options
+            // TODO save and get the information below to/from IP assignment
+            Ip4Address dhcpServerReply = myIP;
+            Ip4Address subnetMaskReply = subnetMask;
+            Ip4Address broadcastReply = broadcastAddress;
 
-            ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(HostId.hostId(packet.getSourceMAC()));
+            // optional options
+            Optional<Ip4Address> routerAddressReply = Optional.of(routerAddress);
+            Optional<Ip4Address> domainServerReply = Optional.of(domainServer);
 
-            if (ipAssignment != null && ipAssignment.rangeNotEnforced()) {
+            IpAssignment ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(
+                    HostId.hostId(packet.getSourceMAC()));
+
+            if (ipAssignment != null &&
+                    ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
                 subnetMaskReply = ipAssignment.subnetMask();
-                dhcpServerReply = ipAssignment.dhcpServer();
-                domainServerReply = ipAssignment.domainServer();
-                routerAddressReply = ipAssignment.routerAddress();
-            } else {
-                subnetMaskReply = subnetMask;
-                dhcpServerReply = myIP;
-                routerAddressReply = routerAddress;
-                domainServerReply = domainServer;
+                broadcastReply = ipAssignment.broadcast();
+                routerAddressReply = Optional.ofNullable(ipAssignment.routerAddress());
+                domainServerReply = Optional.ofNullable(ipAssignment.domainServer());
             }
 
             // Ethernet Frame.
@@ -335,7 +359,7 @@
             List<DHCPOption> optionList = new ArrayList<>();
 
             // DHCP Message Type.
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
+            option.setCode(OptionCode_MessageType.getValue());
             option.setLength((byte) 1);
             byte[] optionData = {outgoingMessageType};
             option.setData(optionData);
@@ -343,13 +367,12 @@
 
             // DHCP Server Identifier.
             option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue());
+            option.setCode(OptionCode_DHCPServerIp.getValue());
             option.setLength((byte) 4);
             option.setData(dhcpServerReply.toOctets());
             optionList.add(option);
 
             if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
-
                 // IP Address Lease Time.
                 option = new DHCPOption();
                 option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
@@ -383,22 +406,26 @@
                 option = new DHCPOption();
                 option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
                 option.setLength((byte) 4);
-                option.setData(broadcastAddress.toOctets());
+                option.setData(broadcastReply.toOctets());
                 optionList.add(option);
 
                 // Router Address.
-                option = new DHCPOption();
-                option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
-                option.setLength((byte) 4);
-                option.setData(routerAddressReply.toOctets());
-                optionList.add(option);
+                if (routerAddressReply.isPresent()) {
+                    option = new DHCPOption();
+                    option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
+                    option.setLength((byte) 4);
+                    option.setData(routerAddressReply.get().toOctets());
+                    optionList.add(option);
+                }
 
                 // DNS Server Address.
-                option = new DHCPOption();
-                option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
-                option.setLength((byte) 4);
-                option.setData(domainServerReply.toOctets());
-                optionList.add(option);
+                if (domainServerReply.isPresent()) {
+                    option = new DHCPOption();
+                    option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
+                    option.setLength((byte) 4);
+                    option.setData(domainServerReply.get().toOctets());
+                    optionList.add(option);
+                }
             }
 
             // End Option.
@@ -439,104 +466,94 @@
          * @param dhcpPayload the extracted DHCP payload
          */
         private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
+            if (dhcpPayload == null) {
+                log.debug("DHCP packet without payload, do nothing");
+                return;
+            }
+
             Ethernet packet = context.inPacket().parsed();
+            DHCPPacketType incomingPacketType = null;
             boolean flagIfRequestedIP = false;
             boolean flagIfServerIP = false;
             Ip4Address requestedIP = Ip4Address.valueOf("0.0.0.0");
             Ip4Address serverIP = Ip4Address.valueOf("0.0.0.0");
 
-            if (dhcpPayload != null) {
-
-                DHCPPacketType incomingPacketType = DHCPPacketType.getType(0);
-                for (DHCPOption option : dhcpPayload.getOptions()) {
-                    if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) {
-                        byte[] data = option.getData();
-                        incomingPacketType = DHCPPacketType.getType(data[0]);
-                    }
-                    if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue()) {
-                        byte[] data = option.getData();
-                        requestedIP = Ip4Address.valueOf(data);
-                        flagIfRequestedIP = true;
-                    }
-                    if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue()) {
-                        byte[] data = option.getData();
-                        serverIP = Ip4Address.valueOf(data);
-                        flagIfServerIP = true;
-                    }
+            for (DHCPOption option : dhcpPayload.getOptions()) {
+                if (option.getCode() == OptionCode_MessageType.getValue()) {
+                    byte[] data = option.getData();
+                    incomingPacketType = DHCPPacketType.getType(data[0]);
                 }
-                DHCPPacketType outgoingPacketType;
-                MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
-                VlanId vlanId = VlanId.vlanId(packet.getVlanID());
-                HostId hostId = HostId.hostId(clientMac, vlanId);
+                if (option.getCode() == OptionCode_RequestedIP.getValue()) {
+                    byte[] data = option.getData();
+                    requestedIP = Ip4Address.valueOf(data);
+                    flagIfRequestedIP = true;
+                }
+                if (option.getCode() == OptionCode_DHCPServerIp.getValue()) {
+                    byte[] data = option.getData();
+                    serverIP = Ip4Address.valueOf(data);
+                    flagIfServerIP = true;
+                }
+            }
 
-                if (incomingPacketType.getValue() == DHCPPacketType.DHCPDISCOVER.getValue()) {
+            if (incomingPacketType == null) {
+                log.debug("No incoming packet type specified, ignore it");
+                return;
+            }
 
-                    outgoingPacketType = DHCPPacketType.DHCPOFFER;
-                    Ip4Address ipOffered = null;
-                    ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
+            DHCPPacketType outgoingPacketType;
+            MacAddress clientMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
+            VlanId vlanId = VlanId.vlanId(packet.getVlanID());
+            HostId hostId = HostId.hostId(clientMac, vlanId);
 
+            switch (incomingPacketType) {
+                case DHCPDISCOVER:
+                    log.trace("DHCP DISCOVER received from {}", hostId);
+                    Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
                     if (ipOffered != null) {
-                        Ethernet ethReply = buildReply(packet, ipOffered,
-                                (byte) outgoingPacketType.getValue());
+                        Ethernet ethReply = buildReply(
+                                packet,
+                                ipOffered,
+                                (byte) DHCPOFFER.getValue());
                         sendReply(context, ethReply);
                     }
-                } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) {
+                    break;
+                case DHCPREQUEST:
+                    log.trace("DHCP REQUEST received from {}", hostId);
+                    if (flagIfServerIP && !myIP.equals(serverIP)) {
+                        return;
+                    }
 
-                    if (flagIfServerIP && flagIfRequestedIP) {
-                        // SELECTING state
+                    if (!flagIfRequestedIP) {
+                        // this is renew or rebinding request
+                        int clientIp = dhcpPayload.getClientIPAddress();
+                        requestedIP = Ip4Address.valueOf(clientIp);
+                    }
 
+                    IpAssignment ipAssignment = IpAssignment.builder()
+                            .ipAddress(requestedIP)
+                            .leasePeriod(leaseTime)
+                            .timestamp(new Date())
+                            .assignmentStatus(Option_Requested).build();
 
-                        if (dhcpStore.getIpAssignmentFromAllocationMap(HostId.hostId(clientMac))
-                                .rangeNotEnforced()) {
-                            outgoingPacketType = DHCPPacketType.DHCPACK;
-                            Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
-                            sendReply(context, ethReply);
-                        } else {
-                            if (myIP.equals(serverIP)) {
-                                if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
-                                    outgoingPacketType = DHCPPacketType.DHCPACK;
-                                    discoverHost(context, requestedIP);
-                                } else {
-                                    outgoingPacketType = DHCPPacketType.DHCPNAK;
-                                }
-                                Ethernet ethReply = buildReply(packet, requestedIP,
-                                        (byte) outgoingPacketType.getValue());
-                                sendReply(context, ethReply);
-                            }
-                        }
-                    } else if (flagIfRequestedIP) {
-                        // INIT-REBOOT state
-                        if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
-                            outgoingPacketType = DHCPPacketType.DHCPACK;
-                            Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
-                            sendReply(context, ethReply);
-                            discoverHost(context, requestedIP);
-                        }
-
+                    if (dhcpStore.assignIP(hostId, ipAssignment)) {
+                        outgoingPacketType = DHCPACK;
+                        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, false, Lists.newArrayList())) {
-                                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);
-                        }
+                        outgoingPacketType = DHCPNAK;
                     }
-                } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) {
-                    Ip4Address ip4Address = dhcpStore.releaseIP(hostId);
-                    if (ip4Address != null) {
-                        hostProviderService.removeIpFromHost(hostId, ip4Address);
+
+                    Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
+                    sendReply(context, ethReply);
+                    break;
+                case DHCPRELEASE:
+                    log.trace("DHCP RELEASE received from {}", hostId);
+                    Ip4Address releaseIp = dhcpStore.releaseIP(hostId);
+                    if (releaseIp != null) {
+                        hostProviderService.removeIpFromHost(hostId, releaseIp);
                     }
-                }
+                    break;
+                default:
+                    break;
             }
         }
 
@@ -575,6 +592,11 @@
          * @param ipAssigned IP Address assigned to the host by DHCP Manager
          */
         private void discoverHost(PacketContext context, Ip4Address ipAssigned) {
+            if (!allowHostDiscovery) {
+                // host discovery is not allowed, do nothing
+                return;
+            }
+
             Ethernet packet = context.inPacket().parsed();
             MacAddress mac = packet.getSourceMAC();
             VlanId vlanId = VlanId.vlanId(packet.getVlanID());
@@ -585,6 +607,8 @@
 
             HostId hostId = HostId.hostId(mac, vlanId);
             DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips);
+
+            log.info("Discovered host {}", desc);
             hostProviderService.hostDetected(hostId, desc, false);
         }
 
diff --git a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java
index a790c8b..38a1905 100644
--- a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java
+++ b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java
@@ -39,9 +39,10 @@
 
 import java.util.Date;
 import java.util.Map;
-import java.util.List;
 import java.util.HashMap;
-import java.util.Objects;
+
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Assigned;
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
 
 /**
  * Manages the pool of available IP Addresses in the network and
@@ -58,18 +59,13 @@
     protected StorageService storageService;
 
     private ConsistentMap<HostId, IpAssignment> allocationMap;
-
     private DistributedSet<Ip4Address> freeIPPool;
 
     private static Ip4Address startIPRange;
-
     private static Ip4Address endIPRange;
 
     // Hardcoded values are default values.
-
     private static int timeoutForPendingAssignments = 60;
-    private static final int MAX_RETRIES = 3;
-    private static final int MAX_BACKOFF = 10;
 
     @Activate
     protected void activate() {
@@ -109,9 +105,9 @@
             IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
             Ip4Address ipAddr = assignmentInfo.ipAddress();
 
-            if (assignmentInfo.rangeNotEnforced()) {
+            if (assignmentInfo.assignmentStatus().equals(Option_RangeNotEnforced)) {
                 return assignmentInfo.ipAddress();
-            } else if (status == IpAssignment.AssignmentStatus.Option_Assigned ||
+            } else if (status == Option_Assigned ||
                     status == IpAssignment.AssignmentStatus.Option_Requested) {
                 // Client has a currently Active Binding.
                 if (ipWithinRange(ipAddr)) {
@@ -166,82 +162,65 @@
     }
 
     @Override
-    public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean rangeNotEnforced,
-                            List<Ip4Address> addressList) {
-        log.debug("Assign IP Called w/ Ip4Address: {}, HostId: {}", ipAddr.toString(), hostId.mac().toString());
+    public boolean assignIP(HostId hostId, IpAssignment ipAssignment) {
+        log.trace("Assign IP Called HostId: {}, ipAssignment: {}",
+                  hostId, ipAssignment);
 
-        Versioned<IpAssignment> currentAssignment = allocationMap.get(hostId);
         IpAssignment newAssignment = null;
-        if (currentAssignment == null) {
-            if (rangeNotEnforced) {
-                newAssignment = IpAssignment.builder()
-                        .ipAddress(ipAddr)
-                        .timestamp(new Date())
-                        .leasePeriod(leaseTime)
-                        .rangeNotEnforced(true)
-                        .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
-                        .subnetMask((Ip4Address) addressList.toArray()[0])
-                        .dhcpServer((Ip4Address) addressList.toArray()[1])
-                        .routerAddress((Ip4Address) addressList.toArray()[2])
-                        .domainServer((Ip4Address) addressList.toArray()[3])
-                        .build();
+        Versioned<IpAssignment> versionedAssignment = allocationMap.get(hostId);
+        Ip4Address requestedIp = ipAssignment.ipAddress();
 
-            } else if (freeIPPool.remove(ipAddr)) {
-                newAssignment = IpAssignment.builder()
-                        .ipAddress(ipAddr)
+        if (versionedAssignment == null) {
+            // this is new IP assignment of static mapping
+            // dynamic assignment is done in suggestIP
+            if (ipAssignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
+                newAssignment = ipAssignment;
+            } else if (freeIPPool.remove(requestedIp)) {
+                newAssignment = IpAssignment.builder(ipAssignment)
+                        .assignmentStatus(Option_Assigned)
                         .timestamp(new Date())
-                        .leasePeriod(leaseTime)
-                        .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
                         .build();
             } else {
+                log.trace("Failed to assign IP for {}", ipAssignment);
                 return false;
             }
+            log.trace("Assigned {}", newAssignment);
             return allocationMap.putIfAbsent(hostId, newAssignment) == null;
             // TODO: handle the case where map changed.
         } else {
-            IpAssignment existingAssignment = currentAssignment.value();
-            if (Objects.equals(existingAssignment.ipAddress(), ipAddr) &&
-                    (existingAssignment.rangeNotEnforced() || ipWithinRange(ipAddr))) {
-                switch (existingAssignment.assignmentStatus()) {
-                    case Option_RangeNotEnforced:
-                        newAssignment = IpAssignment.builder()
-                                .ipAddress(ipAddr)
-                                .timestamp(new Date())
-                                .leasePeriod(existingAssignment.leasePeriod())
-                                .rangeNotEnforced(true)
-                                .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
-                                .subnetMask(existingAssignment.subnetMask())
-                                .dhcpServer(existingAssignment.dhcpServer())
-                                .routerAddress(existingAssignment.routerAddress())
-                                .domainServer(existingAssignment.domainServer())
-                                .build();
-                        break;
-                    case Option_Assigned:
-                    case Option_Requested:
-                        newAssignment = IpAssignment.builder()
-                                .ipAddress(ipAddr)
-                                .timestamp(new Date())
-                                .leasePeriod(leaseTime)
-                                .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
-                                .build();
-                        break;
-                    case Option_Expired:
-                        if (freeIPPool.remove(ipAddr)) {
-                            newAssignment = IpAssignment.builder()
-                                    .ipAddress(ipAddr)
-                                    .timestamp(new Date())
-                                    .leasePeriod(leaseTime)
-                                    .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
-                                    .build();
-                        }
-                        break;
-                    default:
-                        break;
-                }
-                return allocationMap.replace(hostId, currentAssignment.version(), newAssignment);
-            } else {
+            // this is lease renew or rebinding
+            // update assignment status and time stamp, and keep the others
+            IpAssignment existingAssignment = versionedAssignment.value();
+            if (!existingAssignment.ipAddress().equals(requestedIp)) {
+                // return false if existing assignment is not for the
+                // requested host
+                log.trace("Failed to assign IP for {}", ipAssignment);
                 return false;
             }
+
+            switch (existingAssignment.assignmentStatus()) {
+                case Option_RangeNotEnforced:
+                    newAssignment = IpAssignment.builder(existingAssignment)
+                            .timestamp(new Date())
+                            .build();
+                    break;
+                case Option_Expired:
+                    if (!freeIPPool.remove(requestedIp)) {
+                        // requested IP is expired for this host and reserved to the other host
+                        return false;
+                    }
+                case Option_Assigned:
+                case Option_Requested:
+                    newAssignment = IpAssignment.builder(existingAssignment)
+                            .timestamp(new Date())
+                            .assignmentStatus(Option_Assigned)
+                            .build();
+                    break;
+                default:
+                    break;
+            }
+            log.trace("Assigned {}", newAssignment);
+            return allocationMap.replace(hostId, versionedAssignment.version(), newAssignment);
         }
     }
 
@@ -249,8 +228,8 @@
     public Ip4Address releaseIP(HostId hostId) {
         if (allocationMap.containsKey(hostId)) {
             IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
-                                                    .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
-                                                    .build();
+                    .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
+                    .build();
             Ip4Address freeIP = newAssignment.ipAddress();
             allocationMap.put(hostId, newAssignment);
             if (ipWithinRange(freeIP)) {
@@ -273,8 +252,8 @@
         IpAssignment assignment;
         for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
             assignment = entry.getValue().value();
-            if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned
-                    || assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_RangeNotEnforced) {
+            if (assignment.assignmentStatus() == Option_Assigned
+                    || assignment.assignmentStatus() == Option_RangeNotEnforced) {
                 validMapping.put(entry.getKey(), assignment);
             }
         }
@@ -291,10 +270,9 @@
     }
 
     @Override
-    public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean rangeNotEnforced,
-                                  List<Ip4Address> addressList) {
-        HostId host = HostId.hostId(macID);
-        return assignIP(host, ipAddr, -1, rangeNotEnforced, addressList);
+    public boolean assignStaticIP(MacAddress macAddress, IpAssignment ipAssignment) {
+        HostId host = HostId.hostId(macAddress);
+        return assignIP(host, ipAssignment);
     }
 
     @Override
@@ -303,7 +281,7 @@
         if (allocationMap.containsKey(host)) {
             IpAssignment assignment = allocationMap.get(host).value();
 
-            if (assignment.rangeNotEnforced()) {
+            if (assignment.assignmentStatus().equals(Option_RangeNotEnforced)) {
                 allocationMap.remove(host);
                 return true;
             }
@@ -339,11 +317,16 @@
             nextIP = Ip4Address.valueOf(loopCounter);
             freeIPPool.add(nextIP);
         }
+        log.debug("Updated free IP pool {}:{} size:{}", startIP, endIP, freeIPPool.size());
     }
 
     @Override
     public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
-        return allocationMap.get(hostId).value();
+        if (allocationMap.get(hostId) != null) {
+            return allocationMap.get(hostId).value();
+        } else {
+            return null;
+        }
     }
 
     /**
diff --git a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java
index e4c6246..cea58ff 100644
--- a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java
+++ b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java
@@ -18,7 +18,6 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.Lists;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onosproject.dhcp.DhcpService;
@@ -36,8 +35,11 @@
 import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Date;
 import java.util.Map;
 
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_Requested;
+
 /**
  * Manage DHCP address assignments.
  */
@@ -120,10 +122,15 @@
             JsonNode macID = jsonTree.get("mac");
             JsonNode ip = jsonTree.get("ip");
             if (macID != null && ip != null) {
+                IpAssignment ipAssignment = IpAssignment.builder()
+                        .ipAddress(Ip4Address.valueOf(ip.asText()))
+                        .leasePeriod(service.getLeaseTime())
+                        .timestamp(new Date())
+                        .assignmentStatus(Option_Requested)
+                        .build();
 
                 if (!service.setStaticMapping(MacAddress.valueOf(macID.asText()),
-                                              Ip4Address.valueOf(ip.asText()),
-                                              false, Lists.newArrayList())) {
+                                              ipAssignment)) {
                     throw new IllegalArgumentException("Static Mapping Failed. " +
                                                                "The IP maybe unavailable.");
                 }
diff --git a/apps/dhcp/app/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java b/apps/dhcp/app/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java
index 280a5ea..3f27109 100644
--- a/apps/dhcp/app/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java
+++ b/apps/dhcp/app/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java
@@ -28,6 +28,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.UDP;
+import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.CoreServiceAdapter;
 import org.onosproject.dhcp.DhcpStore;
 import org.onosproject.dhcp.IpAssignment;
@@ -93,6 +94,7 @@
         hostProviderService = new TestHostProviderService(new TestHostProvider());
         dhcpXManager.hostProviderService = hostProviderService;
         dhcpXManager.hostProviderRegistry = new TestHostRegistry();
+        dhcpXManager.componentConfigService = new TestComponentConfig();
         dhcpXManager.activate();
     }
 
@@ -228,8 +230,7 @@
             return Ip4Address.valueOf(EXPECTED_IP);
         }
 
-        public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean fromOpenStack,
-                                List<Ip4Address> addressList) {
+        public boolean assignIP(HostId hostId, IpAssignment ipAssignment) {
             return true;
         }
 
@@ -256,8 +257,7 @@
             return map;
         }
 
-        public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean fromOpenStack,
-                                      List<Ip4Address> addressList) {
+        public boolean assignStaticIP(MacAddress macID, IpAssignment ipAssignment) {
             return true;
         }
 
@@ -327,6 +327,13 @@
     }
 
     /**
+     * Mocks the ComponentConfigRegistry.
+     */
+    private class TestComponentConfig extends ComponentConfigAdapter {
+
+    }
+
+    /**
      * Mocks the HostProviderService.
      */
     private class TestHostProviderService extends AbstractProviderService<HostProvider>
diff --git a/apps/dhcp/app/src/test/resources/dhcp-cfg.json b/apps/dhcp/app/src/test/resources/dhcp-cfg.json
index abc48a8..4971fa7 100644
--- a/apps/dhcp/app/src/test/resources/dhcp-cfg.json
+++ b/apps/dhcp/app/src/test/resources/dhcp-cfg.json
@@ -9,14 +9,14 @@
         "router": "10.0.0.1",
         "domain": "10.0.0.1",
         "ttl": "63",
-        "lease": "300",
+        "lease": 300,
         "renew": "150",
-        "rebind": "200",
+        "rebind": 200,
         "delay": "3",
-        "timeout": "150",
+        "timeout": 150,
         "startip": "10.0.0.110",
         "endip": "10.0.0.130"
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java b/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
index 808f2e0..0181bde 100644
--- a/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
+++ b/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.openstacknetworking.switching;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import org.apache.felix.scr.annotations.Activate;
@@ -26,9 +25,11 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.dhcp.DhcpService;
+import org.onosproject.dhcp.IpAssignment;
 import org.onosproject.event.AbstractEvent;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -61,13 +62,18 @@
 import org.onosproject.openstacknetworking.OpenstackSwitchingService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import java.util.List;
+
+import java.util.Date;
 import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 @Service
 @Component(immediate = true)
@@ -117,8 +123,9 @@
     public static final String PORTNAME = "portName";
     private static final String ROUTER_INTERFACE = "network:router_interface";
     public static final String DEVICE_OWNER_GATEWAY = "network:router_gateway";
-    public static final String DNS_SERVER_IP = "8.8.8.8";
+    public static final Ip4Address DNS_SERVER_IP = Ip4Address.valueOf("8.8.8.8");
     private static final String FORWARD_SLASH = "/";
+    private static final int DHCP_INFINITE_LEASE = -1;
 
 
     private ApplicationId appId;
@@ -372,42 +379,41 @@
     }
 
     private void registerDhcpInfo(OpenstackPort openstackPort) {
-        Ip4Address ip4Address, subnetMask, gatewayIPAddress, dhcpServer, domainServer;
-        OpenstackSubnet openstackSubnet;
+        checkNotNull(openstackPort);
+        checkArgument(!openstackPort.fixedIps().isEmpty());
 
-        ip4Address = (Ip4Address) openstackPort.fixedIps().values().stream().findFirst().orElse(null);
-
-        openstackSubnet = openstackService.subnets().stream()
+        OpenstackSubnet openstackSubnet = openstackService.subnets().stream()
                 .filter(n -> n.networkId().equals(openstackPort.networkId()))
-                .findFirst().get();
-
-        subnetMask = Ip4Address.valueOf(buildSubnetMask(openstackSubnet.cidr()));
-        gatewayIPAddress = Ip4Address.valueOf(openstackSubnet.gatewayIp());
-        dhcpServer = gatewayIPAddress;
-        // TODO: supports multiple DNS servers
-        if (openstackSubnet.dnsNameservers().isEmpty()) {
-            domainServer = Ip4Address.valueOf(DNS_SERVER_IP);
-        } else {
-            domainServer = openstackSubnet.dnsNameservers().get(0);
+                .findFirst().orElse(null);
+        if (openstackSubnet == null) {
+            log.warn("Failed to find subnet for {}", openstackPort);
+            return;
         }
-        List<Ip4Address> options = ImmutableList.of(subnetMask, dhcpServer, gatewayIPAddress, domainServer);
 
-        dhcpService.setStaticMapping(openstackPort.macAddress(), ip4Address, true, options);
+        Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get();
+        IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr());
+        Ip4Address broadcast = Ip4Address.makeMaskedAddress(
+                ipAddress,
+                subnetPrefix.prefixLength());
+
+        // TODO: supports multiple DNS servers
+        Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ?
+                DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0);
+
+        IpAssignment ipAssignment = IpAssignment.builder()
+                .ipAddress(ipAddress)
+                .leasePeriod(DHCP_INFINITE_LEASE)
+                .timestamp(new Date())
+                .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength()))
+                .broadcast(broadcast)
+                .domainServer(domainServer)
+                .assignmentStatus(Option_RangeNotEnforced)
+                .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp()))
+                .build();
+
+        dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment);
     }
 
-    private byte[] buildSubnetMask(String cidr) {
-        int prefix;
-        String[] parts = cidr.split(FORWARD_SLASH);
-        prefix = Integer.parseInt(parts[1]);
-        int mask = 0xffffffff << (32 - prefix);
-        byte[] bytes = new byte[]{(byte) (mask >>> 24),
-                (byte) (mask >> 16 & 0xff), (byte) (mask >> 8 & 0xff), (byte) (mask & 0xff)};
-
-        return bytes;
-    }
-
-
-
     private class InternalPacketProcessor implements PacketProcessor {
 
         @Override