ONOS-2197 Adding the skeletal DHCP Server, Packet builder, processor, store

Change-Id: Ie650766e6290f4950445e5ac23f1982e3a8fec57
diff --git a/onos-app-dhcpserver/pom.xml b/onos-app-dhcpserver/pom.xml
new file mode 100644
index 0000000..978b84c
--- /dev/null
+++ b/onos-app-dhcpserver/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2014 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.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  --><project xmlns="http://maven.apache.org/POM/4.0.0"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>onos-app-samples</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.3.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-dhcpserver</artifactId>
+    <packaging>bundle</packaging>
+
+    <url>http://onosproject.org</url>
+
+    <description>DHCP Server application</description>
+
+    <properties>
+        <onos.app.name>org.onosproject.dhcpserver</onos.app.name>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-serializers</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DHCPServer.java b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DHCPServer.java
new file mode 100644
index 0000000..db9d129
--- /dev/null
+++ b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DHCPServer.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.dhcpserver;
+
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.DHCP;
+import org.onlab.packet.DHCPOption;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.UDP;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.onlab.packet.MacAddress.valueOf;
+
+/**
+ * Skeletal ONOS DHCP Server application.
+ */
+@Component(immediate = true)
+public class DHCPServer {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    private DHCPPacketProcessor processor = new DHCPPacketProcessor();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DHCPStore dhcpStore;
+
+    private ApplicationId appId;
+
+    // TODO Make the hardcoded values configurable.
+
+    private static final String MY_IP = "10.0.0.2";
+
+    private static final MacAddress MY_MAC = 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 String subnetMask = "255.0.0.0";
+
+    private static String broadcastAddress = "10.255.255.255";
+
+    @Activate
+    protected void activate() {
+        // start the dhcp server
+        appId = coreService.registerApplication("org.onosproject.dhcpserver");
+
+        packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 10);
+        requestPackets();
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        packetService.removeProcessor(processor);
+
+        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpDst(UDP.DHCP_SERVER_PORT)
+                .matchUdpSrc(UDP.DHCP_CLIENT_PORT);
+
+        packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
+        log.info("Stopped");
+    }
+
+    /**
+     * Request packet in via PacketService.
+     */
+    private void requestPackets() {
+
+        TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpDst(UDP.DHCP_SERVER_PORT)
+                .matchUdpSrc(UDP.DHCP_CLIENT_PORT);
+
+        packetService.requestPackets(selectorServer.build(),
+                PacketPriority.CONTROL, appId);
+    }
+
+    private class DHCPPacketProcessor implements PacketProcessor {
+
+        /**
+         * Builds the DHCP Reply packet.
+         *
+         * @param packet the incoming Ethernet frame
+         * @param ipOffered the IP offered by the DHCP Server
+         * @param outgoingMessageType the message type of the outgoing packet
+         * @return the Ethernet reply frame
+         */
+        private Ethernet buildReply(Ethernet packet, String ipOffered, byte outgoingMessageType) {
+            Ip4Address myIPAddress = Ip4Address.valueOf(MY_IP);
+            Ip4Address ipAddress;
+
+            // Ethernet Frame.
+            Ethernet ethReply = new Ethernet();
+            ethReply.setSourceMACAddress(MY_MAC);
+            ethReply.setDestinationMACAddress(packet.getSourceMAC());
+            ethReply.setEtherType(Ethernet.TYPE_IPV4);
+            ethReply.setVlanID(packet.getVlanID());
+
+            // IP Packet
+            IPv4 ipv4Packet = (IPv4) packet.getPayload();
+            IPv4 ipv4Reply = new IPv4();
+            ipv4Reply.setSourceAddress(myIPAddress.toInt());
+            ipAddress = Ip4Address.valueOf(ipOffered);
+            ipv4Reply.setDestinationAddress(ipAddress.toInt());
+            ipv4Reply.setTtl(packetTTL);
+
+            // UDP Datagram.
+            UDP udpPacket = (UDP) ipv4Packet.getPayload();
+            UDP udpReply = new UDP();
+            udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
+            udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
+
+            // DHCP Payload.
+            DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
+            DHCP dhcpReply = new DHCP();
+            dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
+
+            ipAddress = Ip4Address.valueOf(ipOffered);
+            dhcpReply.setYourIPAddress(ipAddress.toInt());
+            dhcpReply.setServerIPAddress(myIPAddress.toInt());
+
+            dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
+            dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
+            dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
+            dhcpReply.setHardwareAddressLength((byte) 6);
+
+            // DHCP Options.
+            DHCPOption option = new DHCPOption();
+            List<DHCPOption> optionList = new ArrayList<>();
+
+            // DHCP Message Type.
+            option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
+            option.setLength((byte) 1);
+            byte[] optionData = {outgoingMessageType};
+            option.setData(optionData);
+            optionList.add(option);
+
+            // DHCP Server Identifier.
+            option = new DHCPOption();
+            option.setCode(DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue());
+            option.setLength((byte) 4);
+            option.setData(myIPAddress.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);
+
+            // 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 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);
+
+            // Subnet Mask.
+            option = new DHCPOption();
+            option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
+            option.setLength((byte) 4);
+            ipAddress = Ip4Address.valueOf(subnetMask);
+            option.setData(ipAddress.toOctets());
+            optionList.add(option);
+
+            // Broadcast Address.
+            option = new DHCPOption();
+            option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
+            option.setLength((byte) 4);
+            ipAddress = Ip4Address.valueOf(broadcastAddress);
+            option.setData(ipAddress.toOctets());
+            optionList.add(option);
+
+            // Router Address.
+            option = new DHCPOption();
+            option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
+            option.setLength((byte) 4);
+            option.setData(myIPAddress.toOctets());
+            optionList.add(option);
+
+            // DNS Server Address.
+            option = new DHCPOption();
+            option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
+            option.setLength((byte) 4);
+            option.setData(myIPAddress.toOctets());
+            optionList.add(option);
+
+            // End Option.
+            option = new DHCPOption();
+            option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
+            option.setLength((byte) 1);
+            optionList.add(option);
+
+            dhcpReply.setOptions(optionList);
+
+            udpReply.setPayload(dhcpReply);
+            ipv4Reply.setPayload(udpReply);
+            ethReply.setPayload(ipv4Reply);
+
+            return ethReply;
+        }
+
+        /**
+         * Sends the Ethernet reply frame via the Packet Service.
+         *
+         * @param context the context of the incoming frame
+         * @param reply the Ethernet reply frame
+         */
+        private void sendReply(PacketContext context, Ethernet reply) {
+            if (reply != null) {
+                TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+                ConnectPoint sourcePoint = context.inPacket().receivedFrom();
+                builder.setOutput(sourcePoint.port());
+
+                packetService.emit(new DefaultOutboundPacket(sourcePoint.deviceId(),
+                                    builder.build(), ByteBuffer.wrap(reply.serialize())));
+            }
+        }
+
+        /**
+         * Processes the DHCP Payload and initiates a reply to the client.
+         *
+         * @param context context of the incoming message
+         * @param dhcpPayload the extracted DHCP payload
+         */
+        private void processDHCPPacket(PacketContext context, DHCP dhcpPayload) {
+
+            Ethernet packet = context.inPacket().parsed();
+            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) {
+
+                // TODO Convert this to enum value.
+                byte incomingPacketType = 0;
+                for (DHCPOption option : dhcpPayload.getOptions()) {
+                    if (option.getCode() == DHCP.DHCPOptionCode.OptionCode_MessageType.getValue()) {
+                        byte[] data = option.getData();
+                        incomingPacketType = 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;
+                    }
+                }
+
+                String ipOffered = "";
+                DHCP.DHCPMessageType outgoingPacketType;
+                MacAddress clientMAC = new MacAddress(dhcpPayload.getClientHardwareAddress());
+
+                if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Discover.getValue()) {
+
+                    outgoingPacketType = DHCP.DHCPMessageType.MessageType_Offer;
+                    ipOffered = dhcpStore.suggestIP(clientMAC).toString();
+
+                    Ethernet ethReply = buildReply(packet, ipOffered, outgoingPacketType.getValue());
+                    sendReply(context, ethReply);
+
+                } else if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Request.getValue()) {
+
+                    outgoingPacketType = DHCP.DHCPMessageType.MessageType_ACK;
+
+                    if (flagIfServerIP && flagIfRequestedIP) {
+                        // SELECTING state
+                        if (MY_IP.equals(serverIP.toString()) &&
+                                dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) {
+
+                            Ethernet ethReply = buildReply(packet, requestedIP.toString(),
+                                    outgoingPacketType.getValue());
+                            sendReply(context, ethReply);
+                        }
+                    } else if (flagIfRequestedIP) {
+                        // INIT-REBOOT state
+                        if (dhcpStore.assignIP(clientMAC, requestedIP, leaseTime)) {
+                            Ethernet ethReply = buildReply(packet, requestedIP.toString(),
+                                    outgoingPacketType.getValue());
+                            sendReply(context, ethReply);
+                        }
+                    } else {
+                        // RENEWING and REBINDING state
+                        int ciaadr = dhcpPayload.getClientIPAddress();
+                        if (ciaadr != 0) {
+                            Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
+                            if (dhcpStore.assignIP(clientMAC, clientIaddr, leaseTime)) {
+                                Ethernet ethReply = buildReply(packet, clientIaddr.toString(),
+                                        outgoingPacketType.getValue());
+                                sendReply(context, ethReply);
+                            }
+                        }
+                    }
+                } else if (incomingPacketType == DHCP.DHCPMessageType.MessageType_Release.getValue()) {
+
+                    dhcpStore.releaseIP(clientMAC);
+                }
+            }
+        }
+
+        @Override
+        public void process(PacketContext context) {
+
+            // Stop processing if the packet has been handled, since we
+            // can't do any more to it.
+            if (context.isHandled()) {
+                return;
+            }
+
+            Ethernet packet = context.inPacket().parsed();
+            if (packet == null) {
+                return;
+            }
+
+            if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
+                IPv4 ipv4Packet = (IPv4) packet.getPayload();
+
+                if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
+                    UDP udpPacket = (UDP) ipv4Packet.getPayload();
+
+                    if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
+                            udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
+                        // This is meant for the dhcp server so process the packet here.
+
+                        DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
+                        processDHCPPacket(context, dhcpPayload);
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DHCPStore.java b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DHCPStore.java
new file mode 100644
index 0000000..0a3d973
--- /dev/null
+++ b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DHCPStore.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.dhcpserver;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+
+/**
+ * DHCPStore Interface.
+ */
+public interface DHCPStore {
+
+    /**
+     * Returns an IP Address for a Mac ID, in response to a DHCP DISCOVER message.
+     *
+     * @param macID Mac ID of the client requesting an IP
+     * @return IP address assigned to the Mac ID
+     */
+    Ip4Address suggestIP(MacAddress macID);
+
+    /**
+     * Assigns the requested IP to the Mac ID, in response to a DHCP REQUEST message.
+     *
+     * @param macID Mac Id of the client requesting an IP
+     * @param ipAddr IP Address being requested
+     * @param leaseTime Lease time offered by the server for this mapping
+     * @return returns true if the assignment was successful, false otherwise
+     */
+    boolean assignIP(MacAddress macID, Ip4Address ipAddr, long leaseTime);
+
+    /**
+     * Sets the default time for which suggested IP mappings are valid.
+     *
+     * @param timeInSeconds default time for IP mappings to be valid
+     */
+    void setDefaultTimeoutForPurge(long timeInSeconds);
+
+    /**
+     * Releases the IP assigned to a Mac ID into the free pool.
+     *
+     * @param macID the macID for which the mapping needs to be changed
+     */
+    void releaseIP(MacAddress macID);
+
+}
diff --git a/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DistributedDHCPStore.java b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DistributedDHCPStore.java
new file mode 100644
index 0000000..5671fe6
--- /dev/null
+++ b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/DistributedDHCPStore.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.dhcpserver;
+
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Timer;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.DistributedSet;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Manages the pool of available IP Addresses in the network and
+ * Remembers the mapping between MAC ID and IP Addresses assigned.
+ */
+
+@Component(immediate = true)
+@Service
+public class DistributedDHCPStore implements DHCPStore {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private ConsistentMap<MacAddress, IPAssignment> allocationMap;
+
+    private DistributedSet<Ip4Address> freeIPPool;
+
+    private Timeout timeout;
+
+    // TODO Make the hardcoded values configurable.
+
+    private static final int INITIAL_DELAY = 2;
+
+    private static long timeoutForPendingAssignments = 60;
+
+    private static final Ip4Address START_IP = Ip4Address.valueOf("10.1.0.100");
+
+    private static final Ip4Address END_IP = Ip4Address.valueOf("10.1.0.200");
+
+    @Activate
+    protected void activate() {
+        allocationMap = storageService.<MacAddress, IPAssignment>consistentMapBuilder()
+                .withName("onos-dhcp-assignedIP")
+                .withSerializer(Serializer.using(
+                        new KryoNamespace.Builder()
+                                .register(KryoNamespaces.API)
+                                .register(IPAssignment.class,
+                                        IPAssignment.AssignmentStatus.class,
+                                        Date.class,
+                                        long.class,
+                                        Ip4Address.class)
+                                .build()))
+                .build();
+
+        freeIPPool = storageService.<Ip4Address>setBuilder()
+                .withName("onos-dhcp-freeIP")
+                .withSerializer(Serializer.using(KryoNamespaces.API))
+                .build();
+
+        populateIPPoolfromRange(START_IP, END_IP);
+        timeout = Timer.getTimer().newTimeout(new PurgeListTask(), INITIAL_DELAY, TimeUnit.MINUTES);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        timeout.cancel();
+        log.info("Stopped");
+    }
+
+    @Override
+    public Ip4Address suggestIP(MacAddress macID) {
+
+        IPAssignment assignmentInfo;
+        if (allocationMap.containsKey(macID)) {
+            assignmentInfo = allocationMap.get(macID).value();
+            return assignmentInfo.getIpAddress();
+        } else {
+            Ip4Address nextIPAddr = fetchNextIP();
+
+            assignmentInfo = new IPAssignment(nextIPAddr,
+                                            timeoutForPendingAssignments,
+                                            IPAssignment.AssignmentStatus.Option_Requested);
+
+            allocationMap.put(macID, assignmentInfo);
+            return nextIPAddr;
+        }
+    }
+
+    @Override
+    public boolean assignIP(MacAddress macID, Ip4Address ipAddr, long leaseTime) {
+
+        IPAssignment assignmentInfo;
+        if (allocationMap.containsKey(macID)) {
+            assignmentInfo = allocationMap.get(macID).value();
+            if (assignmentInfo.getIpAddress().toInt() == ipAddr.toInt()) {
+                assignmentInfo.setAssignmentStatus(IPAssignment.AssignmentStatus.Option_Assigned);
+                assignmentInfo.setTimestamp(new Date());
+                assignmentInfo.setLeasePeriodinSeconds(leaseTime);
+                allocationMap.put(macID, assignmentInfo);
+                return true;
+            }
+        } else if (freeIPPool.contains(ipAddr)) {
+            assignmentInfo = new IPAssignment(ipAddr, leaseTime, IPAssignment.AssignmentStatus.Option_Assigned);
+            if (freeIPPool.remove(ipAddr)) {
+                allocationMap.put(macID, assignmentInfo);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void releaseIP(MacAddress macID) {
+        if (allocationMap.containsKey(macID)) {
+            Ip4Address freeIP = allocationMap.get(macID).value().getIpAddress();
+            allocationMap.remove(macID);
+            freeIPPool.add(freeIP);
+        }
+    }
+
+    @Override
+    public void setDefaultTimeoutForPurge(long timeInSeconds) {
+        timeoutForPendingAssignments = timeInSeconds;
+    }
+
+    /**
+     * Appends all the IPs in a given range to the free pool of IPs.
+     *
+     * @param startIP Start IP for the range
+     * @param endIP End IP for the range
+     */
+    public void populateIPPoolfromRange(Ip4Address startIP, Ip4Address endIP) {
+        int lastIP = endIP.toInt();
+        Ip4Address nextIP;
+        for (int loopCounter = startIP.toInt(); loopCounter <= lastIP; loopCounter++) {
+            nextIP = Ip4Address.valueOf(loopCounter);
+            freeIPPool.add(nextIP);
+        }
+    }
+
+    /**
+     * Fetches the next available IP from the free pool pf IPs.
+     *
+     * @return the next available IP address
+     */
+    private Ip4Address fetchNextIP() {
+        for (Ip4Address freeIP : freeIPPool) {
+            if (freeIPPool.remove(freeIP)) {
+                return freeIP;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Purges the IP allocation map to remove expired entries and returns the freed IPs to the free pool.
+     */
+    private class PurgeListTask implements TimerTask {
+
+        @Override
+        public void run(Timeout to) {
+            IPAssignment ipAssignment;
+            Date dateNow = new Date();
+            for (Map.Entry<MacAddress, Versioned<IPAssignment>> entry: allocationMap.entrySet()) {
+                ipAssignment = entry.getValue().value();
+                long timeLapsed = dateNow.getTime() - ipAssignment.getTimestamp().getTime();
+                if (timeLapsed > (ipAssignment.getLeasePeriod())) {
+                    Ip4Address freeIP = ipAssignment.getIpAddress();
+                    allocationMap.remove(entry.getKey());
+                    freeIPPool.add(freeIP);
+                }
+            }
+            timeout = Timer.getTimer().newTimeout(new PurgeListTask(), INITIAL_DELAY, TimeUnit.MINUTES);
+        }
+
+    }
+
+}
diff --git a/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/IPAssignment.java b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/IPAssignment.java
new file mode 100644
index 0000000..97cdd87
--- /dev/null
+++ b/onos-app-dhcpserver/src/main/java/org/onosproject/dhcpserver/IPAssignment.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.dhcpserver;
+
+import org.onlab.packet.Ip4Address;
+
+import java.util.Date;
+
+/**
+ * Stores the MAC ID to IP Address mapping details.
+ */
+
+// TODO Convert this into an immutable class using the builder pattern.
+public class IPAssignment {
+
+    private Ip4Address ipAddress;
+
+    private Date timestamp;
+
+    private long leasePeriod;
+
+    private AssignmentStatus assignmentStatus;
+
+    public enum AssignmentStatus {
+        Option_Requested,
+        Option_Assigned,
+        Option_Expired;
+    }
+
+    /**
+     * Default constructor for IPAssignment, where the timestamp is set to the current time.
+     */
+    IPAssignment() {
+        timestamp = new Date();
+    }
+
+    /**
+     * Constructor for IPAssignment, where the ipAddress, the lease period
+     * and assignment status is supplied. The timestamp is set to the current time.
+     *
+     * @param ipAddress
+     * @param leasePeriod
+     * @param assignmentStatus
+     */
+    IPAssignment(Ip4Address ipAddress, long leasePeriod, AssignmentStatus assignmentStatus) {
+        this.ipAddress = ipAddress;
+        this.leasePeriod = leasePeriod;
+        this.assignmentStatus = assignmentStatus;
+
+        this.timestamp = new Date();
+    }
+
+    /**
+     * Sets the IP address for the IP assignment.
+     *
+     * @param ipaddress the assigned IP address
+     */
+    public void setIpAddress(Ip4Address ipaddress) {
+        this.ipAddress = ipaddress;
+    }
+
+    /**
+     * Sets the Timestamp for the IP assignment when the assignment was made.
+     *
+     * @param timestamp timestamp when the assignment was made
+     */
+    public void setTimestamp(Date timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    /**
+     * Sets the assignment status for the IP assignment.
+     *
+     * @param assignmentStatus the assignment status
+     */
+    public void setAssignmentStatus(AssignmentStatus assignmentStatus) {
+        this.assignmentStatus = assignmentStatus;
+    }
+
+    /**
+     * Sets the Lease period value when the argument supplied is in seconds.
+     *
+     * @param leasePeriod lease time in seconds
+     */
+    public void setLeasePeriodinSeconds(long leasePeriod) {
+        this.leasePeriod = leasePeriod * 1000;
+    }
+
+    /**
+     * Sets the Lease period value when the argument supplied is in milliseconds.
+     *
+     * @param leasePeriod lease time in milliseconds
+     */
+    public void setLeasePeriodinMilliseconds(long leasePeriod) {
+        this.leasePeriod = leasePeriod;
+    }
+
+    /**
+     * Returns the IP Address of the IP assignment.
+     *
+     * @return the IP address
+     */
+    public Ip4Address getIpAddress() {
+        return this.ipAddress;
+    }
+
+    /**
+     * Returns the timestamp of the IP assignment.
+     *
+     * @return the timestamp
+     */
+    public Date getTimestamp() {
+        return this.timestamp;
+    }
+
+    /**
+     * Returns the assignment status of the IP assignment.
+     *
+     * @return the assignment status
+     */
+    public AssignmentStatus getAssignmentStatus() {
+        return this.assignmentStatus;
+    }
+
+    /**
+     * Returns the lease period of the IP assignment.
+     *
+     * @return the lease period
+     */
+    public long getLeasePeriod() {
+        return this.leasePeriod;
+    }
+}
diff --git a/onos-app-dhcpserver/src/test/java/org/onosproject/dhcpserver/DHCPServerTest.java b/onos-app-dhcpserver/src/test/java/org/onosproject/dhcpserver/DHCPServerTest.java
new file mode 100644
index 0000000..b91cb81
--- /dev/null
+++ b/onos-app-dhcpserver/src/test/java/org/onosproject/dhcpserver/DHCPServerTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.dhcpserver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Set of tests of the ONOS application component.
+ */
+
+// TODO Add tests later.
+@Ignore
+public class DHCPServerTest {
+
+    private DHCPServer component;
+
+    @Before
+    public void setUp() {
+        component = new DHCPServer();
+        component.activate();
+
+    }
+
+    @After
+    public void tearDown() {
+        component.deactivate();
+    }
+
+    @Test
+    public void basics() {
+
+    }
+
+}