OSPF protocol manual merge from 1.6, due to cherry pick merge conflict

Change-Id: I93653e745468722ce95533537a79e897b4292f5d
diff --git a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfAreaImpl.java b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfAreaImpl.java
index f4c70c0..9a7bd47 100644
--- a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfAreaImpl.java
+++ b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfAreaImpl.java
@@ -21,7 +21,6 @@
 import org.onlab.packet.Ip4Address;
 import org.onosproject.ospf.controller.LsaWrapper;
 import org.onosproject.ospf.controller.OspfArea;
-import org.onosproject.ospf.controller.OspfAreaAddressRange;
 import org.onosproject.ospf.controller.OspfInterface;
 import org.onosproject.ospf.controller.OspfLsa;
 import org.onosproject.ospf.controller.OspfLsaType;
@@ -54,28 +53,14 @@
 public class OspfAreaImpl implements OspfArea {
     private static final Logger log = LoggerFactory.getLogger(OspfAreaImpl.class);
     /**
-     * Address ranges in order to aggregate routing information at area.
-     * boundaries. Each address range is specified by an [address,mask] pair and
-     * a status indication of either Advertise or DoNotAdvertise
-     */
-    private List<OspfAreaAddressRange> addressRanges;
-    /**
-     * This parameter indicates whether the area can carry data traffic that.
-     * neither originates nor terminates in the area itself.
-     */
-    private boolean transitCapability;
-    /**
      * Whether AS-external-LSAs will be flooded into/throughout the area.
      */
     private boolean externalRoutingCapability;
-    /**
-     * Indicates the cost of the default summary-LSA.
-     */
-    private int stubCost;
+
     /**
      * Represents a list of all router's interfaces associated with this area.
      */
-    private List<OspfInterface> interfacesLst;
+    private List<OspfInterface> ospfInterfaceList;
     /**
      * The LS Database for this area. It includes router-LSAs, network-LSAs and.
      * summary-LSAs. AS-external-LSAs are hold in the OSPF class itself.
@@ -116,18 +101,15 @@
         OspfAreaImpl that = (OspfAreaImpl) o;
         return Objects.equal(areaId, that.areaId) &&
                 Objects.equal(routerId, that.routerId) &&
-                Objects.equal(addressRanges.size(), that.addressRanges.size()) &&
-                Objects.equal(transitCapability, that.transitCapability) &&
                 Objects.equal(externalRoutingCapability, that.externalRoutingCapability) &&
-                Objects.equal(stubCost, that.stubCost) &&
-                Objects.equal(interfacesLst.size(), that.interfacesLst.size()) &&
+                Objects.equal(ospfInterfaceList.size(), that.ospfInterfaceList.size()) &&
                 Objects.equal(database, that.database);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(areaId, routerId, addressRanges, transitCapability, externalRoutingCapability,
-                                stubCost, interfacesLst, database);
+        return Objects.hashCode(areaId, routerId, externalRoutingCapability,
+                                ospfInterfaceList, database);
     }
 
     /**
@@ -234,7 +216,7 @@
         networkLsa.setNetworkMask(mask);
         //Adding our own router.
         networkLsa.addAttachedRouter(routerId());
-        Iterator iter = interfacesLst.iterator();
+        Iterator iter = ospfInterfaceList.iterator();
         OspfInterfaceImpl ospfInterface = null;
         while (iter.hasNext()) {
             ospfInterface = (OspfInterfaceImpl) iter.next();
@@ -310,7 +292,7 @@
      */
     private void buildLinkForRouterLsa(RouterLsa routerLsa, OspfInterface ospfInterface) {
         OspfInterfaceImpl nextInterface;
-        Iterator interfaces = interfacesLst.iterator();
+        Iterator interfaces = ospfInterfaceList.iterator();
         while (interfaces.hasNext()) {
             nextInterface = (OspfInterfaceImpl) interfaces.next();
             if (nextInterface.state() == OspfInterfaceState.DOWN) {
@@ -418,44 +400,6 @@
     }
 
     /**
-     * Gets address range.
-     *
-     * @return list of area address ranges
-     */
-    public List<OspfAreaAddressRange> addressRanges() {
-        return addressRanges;
-    }
-
-    /**
-     * Sets the area address ranges.
-     *
-     * @param addressRanges list of area address range
-     */
-    @JsonProperty("addressRange")
-    public void setAddressRanges(List<OspfAreaAddressRange> addressRanges) {
-        this.addressRanges = addressRanges;
-    }
-
-    /**
-     * Gets is transit capable or not.
-     *
-     * @return true if transit capable, else false
-     */
-    public boolean isTransitCapability() {
-        return transitCapability;
-    }
-
-    /**
-     * Sets transit capability.
-     *
-     * @param transitCapability true if transit capable, else false
-     */
-    @JsonProperty("transitCapability")
-    public void setTransitCapability(boolean transitCapability) {
-        this.transitCapability = transitCapability;
-    }
-
-    /**
      * Gets external routing capability.
      *
      * @return true if external routing capable, else false
@@ -475,41 +419,22 @@
     }
 
     /**
-     * Gets the stub cost.
-     *
-     * @return stub cost
-     */
-    public int stubCost() {
-        return stubCost;
-    }
-
-    /**
-     * Sets the stub cost.
-     *
-     * @param stubCost stub cost
-     */
-    @JsonProperty("stubCost")
-    public void setStubCost(int stubCost) {
-        this.stubCost = stubCost;
-    }
-
-    /**
      * Gets the list of interfaces in this area.
      *
      * @return list of interfaces
      */
-    public List<OspfInterface> getInterfacesLst() {
-        return interfacesLst;
+    public List<OspfInterface> ospfInterfaceList() {
+        return ospfInterfaceList;
     }
 
     /**
      * Sets the list of interfaces attached to the area.
      *
-     * @param interfacesLst list of OspfInterface instances
+     * @param ospfInterfaceList list of OspfInterface instances
      */
     @JsonProperty("interface")
-    public void setInterfacesLst(List<OspfInterface> interfacesLst) {
-        this.interfacesLst = interfacesLst;
+    public void setOspfInterfaceList(List<OspfInterface> ospfInterfaceList) {
+        this.ospfInterfaceList = ospfInterfaceList;
     }
 
     /**
@@ -522,7 +447,7 @@
     public boolean noNeighborInLsaExchangeProcess() {
         OspfInterfaceImpl nextInterface;
         OspfNeighborState nextNeighborState;
-        Iterator interfaces = interfacesLst.iterator();
+        Iterator interfaces = ospfInterfaceList.iterator();
         while (interfaces.hasNext()) {
             nextInterface = (OspfInterfaceImpl) interfaces.next();
             Iterator neighbors = nextInterface.listOfNeighbors().values().iterator();
@@ -659,19 +584,14 @@
         return MoreObjects.toStringHelper(getClass())
                 .omitNullValues()
                 .add("areaID", areaId)
-                .add("stubCost", stubCost)
-                .add("addressRanges", addressRanges)
-                .add("interfacesLst", interfacesLst)
-                .add("transitCapability", transitCapability)
+                .add("ospfInterfaceList", ospfInterfaceList)
                 .add("externalRoutingCapability", externalRoutingCapability)
                 .toString();
     }
 
     /**
      * Checks all Neighbors belonging to this Area whether they are in state lesser than the EXCHANGE.
-     * <p>
      * Creates list of such neighbors
-     * <p>
      * Returns list of neighbors who satisfy the conditions
      *
      * @param ospfInterface OSPF interface instance
@@ -716,7 +636,7 @@
     public void addToOtherNeighborLsaTxList(LsaHeader recLsa) {
         //Add the received LSA in other neighbors retransmission list.
         log.debug("OspfAreaImpl: addToOtherNeighborLsaTxList");
-        List<OspfInterface> ospfInterfaces = getInterfacesLst();
+        List<OspfInterface> ospfInterfaces = ospfInterfaceList();
         for (OspfInterface ospfInterfaceFromArea : ospfInterfaces) {
             Map neighbors = ospfInterfaceFromArea.listOfNeighbors();
             for (Object neighborIP : neighbors.keySet()) {
diff --git a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java
index 19f8ef5..b10cf33 100644
--- a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java
+++ b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java
@@ -18,47 +18,93 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelHandlerContext;
 import org.onlab.packet.Ip4Address;
+import org.onosproject.ospf.controller.LsaWrapper;
+import org.onosproject.ospf.controller.OspfArea;
 import org.onosproject.ospf.controller.OspfInterface;
+import org.onosproject.ospf.controller.OspfLinkTed;
+import org.onosproject.ospf.controller.OspfLsa;
+import org.onosproject.ospf.controller.OspfMessage;
 import org.onosproject.ospf.controller.OspfNbr;
+import org.onosproject.ospf.controller.OspfNeighborState;
+import org.onosproject.ospf.controller.OspfPacketType;
+import org.onosproject.ospf.controller.OspfRouter;
+import org.onosproject.ospf.controller.TopologyForDeviceAndLink;
+import org.onosproject.ospf.controller.impl.Controller;
+import org.onosproject.ospf.controller.impl.OspfNbrImpl;
+import org.onosproject.ospf.controller.impl.TopologyForDeviceAndLinkImpl;
+import org.onosproject.ospf.controller.lsdb.LsaWrapperImpl;
+import org.onosproject.ospf.controller.lsdb.OspfLsdbImpl;
+import org.onosproject.ospf.controller.util.OspfEligibleRouter;
+import org.onosproject.ospf.controller.util.OspfInterfaceType;
 import org.onosproject.ospf.protocol.lsa.LsaHeader;
 import org.onosproject.ospf.protocol.lsa.OpaqueLsaHeader;
+import org.onosproject.ospf.protocol.ospfpacket.OspfMessageWriter;
+import org.onosproject.ospf.protocol.ospfpacket.OspfPacketHeader;
+import org.onosproject.ospf.protocol.ospfpacket.subtype.LsRequestPacket;
+import org.onosproject.ospf.protocol.ospfpacket.types.DdPacket;
+import org.onosproject.ospf.protocol.ospfpacket.types.HelloPacket;
+import org.onosproject.ospf.protocol.ospfpacket.types.LsAcknowledge;
+import org.onosproject.ospf.protocol.ospfpacket.types.LsRequest;
+import org.onosproject.ospf.protocol.ospfpacket.types.LsUpdate;
+import org.onosproject.ospf.protocol.util.ChecksumCalculator;
 import org.onosproject.ospf.protocol.util.OspfInterfaceState;
 import org.onosproject.ospf.protocol.util.OspfParameters;
+import org.onosproject.ospf.protocol.util.OspfUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Representation of an OSPF interface.
  */
 public class OspfInterfaceImpl implements OspfInterface {
     private static final Logger log = LoggerFactory.getLogger(OspfInterfaceImpl.class);
+    private int interfaceIndex;
     private Ip4Address ipAddress;
     private Ip4Address ipNetworkMask;
-    private int areaId;
+    private Channel channel = null;
     private int helloIntervalTime;
     private int routerDeadIntervalTime;
-    private int transmitDelay;
     private int routerPriority;
-    private int systemInterfaceType;
     private int interfaceType;
-    private int interfaceCost;
-    private String authType;
-    private String authKey;
-    private int pollInterval;
     private int mtu;
     private int reTransmitInterval;
     private Ip4Address dr;
     private Ip4Address bdr;
     private OspfInterfaceState state;
     private List<LsaHeader> linkStateHeaders = new ArrayList<>();
-    private HashMap<String, OspfNbr> listOfNeighbors = new HashMap<>();
-    private HashMap<String, LsaHeader> listOfNeighborMap = new HashMap<>();
+    private Map<String, OspfNbr> listOfNeighbors = new ConcurrentHashMap<>();
+    private Map<String, LsaHeader> listOfNeighborMap = new ConcurrentHashMap<>();
+    private long delay = 0;
+    private InternalHelloTimer helloTimerTask;
+    private InternalWaitTimer waitTimerTask;
+    private InternalDelayedAckTimer delayedAckTimerTask;
+    private ScheduledExecutorService exServiceHello;
+    private ScheduledExecutorService exServiceWait;
+    private ScheduledExecutorService exServiceDelayedAck;
+    private boolean isDelayedAckTimerScheduled = false;
+    private int delayedAckTimerInterval = 2500;
+    private int interfaceTypeOldValue = 0;
+    private TopologyForDeviceAndLink topologyForDeviceAndLink = new TopologyForDeviceAndLinkImpl();
+    private OspfArea ospfArea;
+    private Controller controller;
+
 
     /**
      * Gets the interface state.
@@ -79,6 +125,51 @@
     }
 
     /**
+     * Sets the netty channel.
+     *
+     * @param channel channel
+     */
+    public void setChannel(Channel channel) {
+        this.channel = channel;
+    }
+
+    /**
+     * Returns OSPF area instance.
+     *
+     * @return OSPF area instance
+     */
+    public OspfArea ospfArea() {
+        return ospfArea;
+    }
+
+    /**
+     * Sets OSPF controller instance.
+     *
+     * @param controller OSPF controller instance
+     */
+    public void setController(Controller controller) {
+        this.controller = controller;
+    }
+
+    /**
+     * Sets OSPF area instance.
+     *
+     * @param ospfArea OSPF area instance
+     */
+    public void setOspfArea(OspfArea ospfArea) {
+        this.ospfArea = ospfArea;
+    }
+
+    /**
+     * Gets interface state.
+     *
+     * @return interface state
+     */
+    public String interfaceState() {
+        return state.interfaceState();
+    }
+
+    /**
      * Gets link state headers.
      *
      * @return get the list of lsa headers
@@ -130,9 +221,36 @@
         return listOfNeighbors.get(neighborId);
     }
 
+    /**
+     * Removes all the neighbors.
+     */
+    public void removeNeighbors() {
+        Set<String> neighbors = listOfNeighbors.keySet();
+        for (String neighborId : neighbors) {
+            removeNeighbor(listOfNeighbors.get(neighborId));
+            log.debug("Neighbor removed - {}", neighborId);
+        }
+        listOfNeighbors.clear();
+    }
 
     /**
-     * Adds LSAHeader to map.
+     * Removes neighbor from the interface neighbor map.
+     *
+     * @param ospfNeighbor OSPF neighbor instance
+     */
+    public void removeNeighbor(OspfNbr ospfNeighbor) {
+        log.debug("Neighbor removed - {}", ospfNeighbor.neighborId());
+        ospfNeighbor.stopInactivityTimeCheck();
+        ospfNeighbor.stopFloodingTimer();
+        ospfNeighbor.stopRxMtDdTimer();
+        ospfNeighbor.stopRxMtLsrTimer();
+
+        listOfNeighbors.remove(ospfNeighbor.neighborId());
+    }
+
+
+    /**
+     * Adds LSA header to map.
      *
      * @param lsaHeader LSA header instance
      */
@@ -175,7 +293,7 @@
      *
      * @return listOfNeighbors as key value pair
      */
-    public HashMap<String, OspfNbr> listOfNeighbors() {
+    public Map<String, OspfNbr> listOfNeighbors() {
         return listOfNeighbors;
     }
 
@@ -189,6 +307,24 @@
     }
 
     /**
+     * Returns interface index.
+     *
+     * @return interface index
+     */
+    public int interfaceIndex() {
+        return interfaceIndex;
+    }
+
+    /**
+     * Set interface index.
+     *
+     * @param interfaceIndex interface index
+     */
+    public void setInterfaceIndex(int interfaceIndex) {
+        this.interfaceIndex = interfaceIndex;
+    }
+
+    /**
      * Gets the IP address.
      *
      * @return IP address
@@ -225,25 +361,6 @@
     }
 
     /**
-     * Gets the area id this interface belongs.
-     *
-     * @return area id this interface belongs
-     */
-    public int areaId() {
-        return areaId;
-    }
-
-
-    /**
-     * Sets the area id this interface belongs.
-     *
-     * @param areaId the area id this interface belongs
-     */
-    public void setAreaId(int areaId) {
-        this.areaId = areaId;
-    }
-
-    /**
      * Gets hello interval time.
      *
      * @return hello interval time
@@ -298,78 +415,6 @@
     }
 
     /**
-     * Gets interface cost.
-     *
-     * @return interface cost
-     */
-    public int interfaceCost() {
-        return interfaceCost;
-    }
-
-    /**
-     * Sets interface cost.
-     *
-     * @param interfaceCost interface cost
-     */
-    public void setInterfaceCost(int interfaceCost) {
-        this.interfaceCost = interfaceCost;
-    }
-
-    /**
-     * Gets authentication type.
-     *
-     * @return authType represents authentication type
-     */
-    public String authType() {
-        return authType;
-    }
-
-    /**
-     * Sets authentication type.
-     *
-     * @param authType authType represents authentication type
-     */
-    public void setAuthType(String authType) {
-        this.authType = authType;
-    }
-
-    /**
-     * Gets authentication key.
-     *
-     * @return authKey represents authentication key
-     */
-    public String authKey() {
-        return authKey;
-    }
-
-    /**
-     * Sets authentication key.
-     *
-     * @param authKey represents authentication key
-     */
-    public void setAuthKey(String authKey) {
-        this.authKey = authKey;
-    }
-
-    /**
-     * Gets poll interval.
-     *
-     * @return pollInterval an integer represents poll interval
-     */
-    public int pollInterval() {
-        return pollInterval;
-    }
-
-    /**
-     * Sets poll interval.
-     *
-     * @param pollInterval an integer represents poll interval
-     */
-    public void setPollInterval(int pollInterval) {
-        this.pollInterval = pollInterval;
-    }
-
-    /**
      * Gets max transfer unit.
      *
      * @return mtu an integer represents max transfer unit
@@ -442,23 +487,1070 @@
     }
 
     /**
-     * Get transmission delay.
+     * Represents an interface is up and connected.
      *
-     * @return transmission delay
+     * @throws Exception might throws exception
      */
-    public int transmitDelay() {
-        return transmitDelay;
+    public void interfaceUp() throws Exception {
+        log.debug("OSPFInterfaceChannelHandler::interfaceUp...!!!");
+        if (interfaceType() == OspfInterfaceType.POINT_TO_POINT.value()) {
+            setState(OspfInterfaceState.POINT2POINT);
+            interfaceTypeOldValue = interfaceType();
+            log.debug("OSPFInterfaceChannelHandler::InterfaceType {} state {} ",
+                      interfaceType(), state());
+        } else if (interfaceType() == OspfInterfaceType.BROADCAST.value()) {
+            //if router priority is 0, move the state to DROther
+            interfaceTypeOldValue = interfaceType();
+            if (routerPriority() == 0) {
+                setState(OspfInterfaceState.DROTHER);
+            } else {
+                log.debug("OSPFInterfaceChannelHandler::InterfaceType {} state {} RouterPriority {}",
+                          interfaceType(),
+                          state(), routerPriority());
+                setState(OspfInterfaceState.WAITING);
+                //start wait timer - like inactivity timer with router deadInterval
+                startWaitTimer();
+            }
+        }
+        // Start hello timer with interval from config - convert seconds to milliseconds
+        startHelloTimer();
+        ospfArea.refreshArea(this);
+    }
+
+
+    /**
+     * Gets called when a BDR was detected before the wait timer expired.
+     *
+     * @param ch channel instance
+     * @throws Exception might throws exception
+     */
+    public void backupSeen(Channel ch) throws Exception {
+        log.debug("OSPFInterfaceChannelHandler::backupSeen ");
+        if (state() == OspfInterfaceState.WAITING) {
+            electRouter(ch);
+        }
     }
 
     /**
-     * Sets transmission delay.
+     * Gets called when no hello message received for particular period.
      *
-     * @param transmitDelay transmission delay
+     * @param ch channel instance
+     * @throws Exception might throws exception
      */
-    public void setTransmitDelay(int transmitDelay) {
-        this.transmitDelay = transmitDelay;
+    public void waitTimer(Channel ch) throws Exception {
+        log.debug("OSPFInterfaceChannelHandler::waitTimer ");
+        //According to RFC-2328 section 9.4
+        if (state() == OspfInterfaceState.WAITING) {
+            electRouter(ch);
+        }
     }
 
+    /**
+     * Initiates DR election process.
+     *
+     * @param ch netty channel instance
+     * @throws Exception might throws exception
+     */
+    public void callDrElection(Channel ch) throws Exception {
+        log.debug("OSPFInterfaceChannelHandler::callDrElection ");
+        //call when timer expired
+        //no hello message received for particular interval
+        //section 9.4
+        electRouter(ch);
+        interfaceTypeOldValue = interfaceType();
+    }
+
+    /**
+     * Neighbor change event is triggered when the router priority gets changed.
+     *
+     * @throws Exception might throws exception
+     */
+    public void neighborChange() throws Exception {
+        log.debug("OSPFInterfaceChannelHandler::neighborChange ");
+        if (state() == OspfInterfaceState.DR || state() == OspfInterfaceState.BDR ||
+                state() == OspfInterfaceState.DROTHER) {
+            electRouter(channel);
+        }
+    }
+
+    /**
+     * Gets called when an interface is down.
+     * All interface variables are reset, and interface timers disabled.
+     * Also all neighbor connections associated with the interface are destroyed.
+     */
+    public void interfaceDown() {
+        log.debug("OSPFInterfaceChannelHandler::interfaceDown ");
+        stopHelloTimer();
+        listOfNeighbors().clear();
+        setState(OspfInterfaceState.DOWN);
+    }
+
+    /**
+     * When an OSPF message received it is handed over to this method.
+     * Based on the type of the OSPF message received it will be handed over
+     * to corresponding message handler methods.
+     *
+     * @param ospfMessage received OSPF message
+     * @param ctx         channel handler context instance.
+     * @throws Exception might throws exception
+     */
+    public void processOspfMessage(OspfMessage ospfMessage, ChannelHandlerContext ctx) throws Exception {
+        log.debug("OspfChannelHandler::processOspfMessage...!!!");
+
+        if (!validateMessage(ospfMessage)) {
+            return;
+        }
+
+        switch (ospfMessage.ospfMessageType().value()) {
+            case OspfParameters.HELLO:
+                processHelloMessage(ospfMessage, ctx);
+                break;
+            case OspfParameters.DD:
+                processDdMessage(ospfMessage, ctx);
+                break;
+            case OspfParameters.LSREQUEST:
+                processLsRequestMessage(ospfMessage, ctx);
+                break;
+            case OspfParameters.LSUPDATE:
+                processLsUpdateMessage(ospfMessage, ctx);
+                break;
+            case OspfParameters.LSACK:
+                processLsAckMessage(ospfMessage, ctx);
+                break;
+            default:
+                log.debug("Unknown packet to process...!!!");
+                break;
+        }
+    }
+
+    /**
+     * Validates the OSPF message received.
+     *
+     * @param ospfMessage OSPF message.
+     * @return true if it is a valid else false.
+     * @throws Exception might throws exception
+     */
+    private boolean validateMessage(OspfMessage ospfMessage) throws Exception {
+        boolean isValid = true;
+        OspfPacketHeader header = (OspfPacketHeader) ospfMessage;
+
+        //added the check to eliminate self origin packets also two interfaces on same router.
+        if (!header.sourceIp().equals(ipAddress()) && !header.routerId().equals(
+                ospfArea.routerId())) {
+            //Verify the checksum
+            ChecksumCalculator checksum = new ChecksumCalculator();
+            if (!checksum.isValidOspfCheckSum(ospfMessage, OspfUtil.OSPFPACKET_CHECKSUM_POS1,
+                                              OspfUtil.OSPFPACKET_CHECKSUM_POS2)) {
+                log.debug("Checksum mismatch. Received packet type {} ", ospfMessage.ospfMessageType());
+                return false;
+            }
+            if (((OspfPacketHeader) ospfMessage).ospfVersion() != OspfUtil.OSPF_VERSION_2) {
+                log.debug("Received osfpMessage Version should match with Interface Version ");
+                return false;
+            }
+            if (!((OspfPacketHeader) ospfMessage).areaId().equals(ospfArea.areaId())) {
+                log.debug("Received ospf packets are from different area than our Area ID. " +
+                                  "Received Area ID {}, Our AreaId {} ",
+                          ((OspfPacketHeader) ospfMessage).areaId(), ospfArea.areaId());
+                return false;
+            }
+
+            //According to RFC-2328 (8.2)
+            /**
+             * ABR should receive packets from backbone 0.0.0.0 as we are not acting as ABR
+             * we are rejecting the packet.
+             */
+            if (((OspfPacketHeader) ospfMessage).areaId().equals(Ip4Address.valueOf("0.0.0.0"))) {
+                log.debug("ABR should receive packets from backbone 0.0.0.0 as we are not acting as " +
+                                  "ABR we are rejecting the ospf packet");
+                return false;
+            }
+            if (interfaceType() == OspfInterfaceType.BROADCAST.value() &&
+                    !OspfUtil.sameNetwork(((OspfPacketHeader) ospfMessage).sourceIp(),
+                                          ipAddress(), ipNetworkMask())) {
+                log.debug("Received packets from different subnets. Discarding...!!!");
+                return false;
+            }
+        } else {
+            isValid = false;
+        }
+
+        return isValid;
+    }
+
+    /**
+     * Processes Hello message.
+     *
+     * @param ospfMessage OSPF message instance.
+     * @param ctx         context instance.
+     * @throws Exception might throws exception
+     */
+    void processHelloMessage(OspfMessage ospfMessage, ChannelHandlerContext ctx) throws Exception {
+        Channel channel = ctx.getChannel();
+        log.debug("OspfChannelHandler::processHelloMessage...!!!");
+        HelloPacket helloPacket = (HelloPacket) ospfMessage;
+
+        // processing of hello packet as per RFC 2328 section 10.5
+        log.debug("OspfChannelHandler::processHelloMessage::Interface Type {} OSPFInterfaceState {} ",
+                  interfaceType(), state());
+
+        if (interfaceType() != OspfInterfaceType.POINT_TO_POINT.value()) {
+            if (!helloPacket.networkMask().equals(ipNetworkMask())) {
+                log.debug("OspfChannelHandler::processHelloMessage::Hello Packet Received does not " +
+                                  "match the same network mask as the configure Interface");
+                return;
+            }
+        }
+        if (helloPacket.helloInterval() != helloIntervalTime()) {
+            log.debug("OspfChannelHandler::processHelloMessage::Hello Packet Received have the same " +
+                              "hello interval as configured Interface");
+            return;
+        }
+        if (helloPacket.routerDeadInterval() != routerDeadIntervalTime()) {
+            log.debug("OspfChannelHandler::processHelloMessage::Hello Packet Received have the same " +
+                              "Router Dead interval as configured Interface");
+            return;
+        }
+
+        if (interfaceType == OspfInterfaceType.POINT_TO_POINT.value() &&
+                !helloPacket.dr().equals(OspfUtil.DEFAULTIP)) {
+            log.debug("OspfChannelHandler::processHelloMessage:: Neighbor in broadcast network");
+            return;
+        }
+
+        if (interfaceType == OspfInterfaceType.POINT_TO_POINT.value()) {
+            // to verify if the neighbor which sent the hello is present in the OSPF Interface neighboring list .
+            OspfNbr nbr;
+            if (!isNeighborInList(helloPacket.routerId().toString())) {
+                nbr = new OspfNbrImpl(ospfArea, this, helloPacket.sourceIp(),
+                                      helloPacket.routerId(), helloPacket.options(), topologyForDeviceAndLink);
+                addNeighbouringRouter(nbr);
+            } else {
+                nbr = neighbouringRouter(helloPacket.routerId().toString());
+                nbr.setRouterPriority(helloPacket.routerPriority());
+            }
+            if (!helloPacket.containsNeighbour(ospfArea.routerId())) {
+                ((OspfNbrImpl) nbr).oneWayReceived(helloPacket, channel);
+            } else {
+                ((OspfNbrImpl) nbr).twoWayReceived(helloPacket, ctx.getChannel());
+            }
+        } else if (interfaceType == OspfInterfaceType.BROADCAST.value()) {
+
+            if (state() == OspfInterfaceState.WAITING) {
+                if ((!helloPacket.dr().equals(Ip4Address.valueOf("0.0.0.0"))) &&
+                        (!helloPacket.bdr().equals(Ip4Address.valueOf("0.0.0.0")))) {
+                    stopWaitTimer();
+                    setDr(helloPacket.dr());
+                    setBdr(helloPacket.bdr());
+                    if (helloPacket.dr().equals(ipAddress())) {
+                        setState(OspfInterfaceState.DR);
+                        //refresh router Lsa
+                        ospfArea.refreshArea(this);
+                    } else if (helloPacket.bdr().equals(ipAddress())) {
+                        setState(OspfInterfaceState.BDR);
+                        //refresh router Lsa
+                        ospfArea.refreshArea(this);
+                    } else {
+                        setState(OspfInterfaceState.DROTHER);
+                        ospfArea.refreshArea(this);
+                    }
+
+                } else if (!helloPacket.dr().equals(Ip4Address.valueOf("0.0.0.0")) ||
+                        !helloPacket.bdr().equals(Ip4Address.valueOf("0.0.0.0"))) {
+                    setDr(helloPacket.dr());
+                    setBdr(helloPacket.bdr());
+                }
+                Ip4Address sourceIp = helloPacket.sourceIp();
+                OspfNbr nbr;
+                if (!isNeighborInList(helloPacket.routerId().toString())) {
+                    nbr = new OspfNbrImpl(ospfArea, this, sourceIp, helloPacket.routerId(),
+                                          helloPacket.options(), topologyForDeviceAndLink);
+                    nbr.setNeighborId(helloPacket.routerId());
+                    nbr.setNeighborBdr(helloPacket.bdr());
+                    nbr.setNeighborDr(helloPacket.dr());
+                    nbr.setRouterPriority(helloPacket.routerPriority());
+                    addNeighbouringRouter(nbr);
+                } else {
+                    nbr = neighbouringRouter(helloPacket.routerId().toString());
+                    nbr.setRouterPriority(helloPacket.routerPriority());
+                }
+                if (!helloPacket.containsNeighbour(ospfArea.routerId())) {
+                    ((OspfNbrImpl) nbr).oneWayReceived(helloPacket, channel);
+                } else {
+                    ((OspfNbrImpl) nbr).twoWayReceived(helloPacket, ctx.getChannel());
+                }
+
+                if (helloPacket.dr().equals(sourceIp)) {
+                    if (helloPacket.bdr().equals(Ip4Address.valueOf("0.0.0.0"))) {
+                        // call backup seen
+                        stopWaitTimer();
+                        backupSeen(ctx.getChannel());
+                    }
+                }
+
+                if (helloPacket.bdr().equals(sourceIp)) {
+                    // call backup seen
+                    stopWaitTimer();
+                    backupSeen(ctx.getChannel());
+                }
+            } else {
+
+                if ((!helloPacket.dr().equals(Ip4Address.valueOf("0.0.0.0")) ||
+                        !helloPacket.bdr().equals(Ip4Address.valueOf("0.0.0.0")))
+                        && routerPriority() == 0) {
+                    setDr(helloPacket.dr());
+                    setBdr(helloPacket.bdr());
+                }
+                //To verify if the neighbor which sent the hello is present in the OSPF Interface neighboring list .
+                Ip4Address sourceIp = helloPacket.sourceIp();
+                OspfNbr nbr;
+                if (!isNeighborInList(helloPacket.routerId().toString())) {
+                    nbr = new OspfNbrImpl(ospfArea, this, sourceIp, helloPacket.routerId(),
+                                          helloPacket.options(), topologyForDeviceAndLink);
+                    nbr.setNeighborId(helloPacket.routerId());
+                    nbr.setNeighborBdr(helloPacket.bdr());
+                    nbr.setNeighborDr(helloPacket.dr());
+                    nbr.setRouterPriority(helloPacket.routerPriority());
+                    addNeighbouringRouter(nbr);
+                    ((OspfNbrImpl) nbr).oneWayReceived(helloPacket, channel);
+                } else {
+                    log.debug("OspfChannelHandler::NeighborInList::helloPacket.bdr(): {}, " +
+                                      "helloPacket.dr(): {}", helloPacket.bdr(), helloPacket.dr());
+                    nbr = neighbouringRouter(helloPacket.routerId().toString());
+                    nbr.setRouterPriority(helloPacket.routerPriority());
+                    if (!helloPacket.containsNeighbour(ospfArea.routerId())) {
+                        ((OspfNbrImpl) nbr).oneWayReceived(helloPacket, channel);
+                    } else {
+                        ((OspfNbrImpl) nbr).twoWayReceived(helloPacket, ctx.getChannel());
+                    }
+                    if (nbr.routerPriority() != helloPacket.routerPriority()) {
+                        nbr.setNeighborBdr(helloPacket.bdr());
+                        nbr.setNeighborDr(helloPacket.dr());
+                        neighborChange();
+                    }
+
+
+                    if (nbr.neighborIpAddr().equals(helloPacket.dr()) &&
+                            !(nbr.neighborIpAddr().equals(nbr.neighborDr()))) {
+                        nbr.setNeighborBdr(helloPacket.bdr());
+                        nbr.setNeighborDr(helloPacket.dr());
+                        neighborChange();
+                    }
+
+                    if (!(nbr.neighborIpAddr().equals(helloPacket.dr())) &&
+                            (nbr.neighborIpAddr().equals(nbr.neighborDr()))) {
+                        nbr.setNeighborBdr(helloPacket.bdr());
+                        nbr.setNeighborDr(helloPacket.dr());
+                        neighborChange();
+                    }
+
+                    if (nbr.neighborIpAddr().equals(helloPacket.bdr()) &&
+                            !(nbr.neighborIpAddr().equals(nbr.neighborBdr()))) {
+                        nbr.setNeighborBdr(helloPacket.bdr());
+                        nbr.setNeighborDr(helloPacket.dr());
+                        neighborChange();
+                    }
+
+                    if (!(nbr.neighborIpAddr().equals(helloPacket.bdr())) &&
+                            (nbr.neighborIpAddr().equals(nbr.neighborBdr()))) {
+                        nbr.setNeighborBdr(helloPacket.bdr());
+                        nbr.setNeighborDr(helloPacket.dr());
+                        neighborChange();
+                    }
+
+                    nbr.setNeighborBdr(helloPacket.bdr());
+                    nbr.setNeighborDr(helloPacket.dr());
+                }
+            }
+        }
+    }
+
+    /**
+     * process the DD message which received.
+     *
+     * @param ospfMessage OSPF message instance.
+     * @param ctx         channel handler context instance
+     * @throws Exception might throws exception
+     */
+    void processDdMessage(OspfMessage ospfMessage, ChannelHandlerContext ctx) throws Exception {
+        log.debug("OspfChannelHandler::processDdMessage...!!!");
+        Channel channel = ctx.getChannel();
+        DdPacket ddPacket = (DdPacket) ospfMessage;
+        log.debug("Got DD packet from {}", ddPacket.sourceIp());
+        //check it is present in listOfNeighbors
+        Ip4Address neighbourId = ddPacket.routerId();
+        OspfNbr nbr = neighbouringRouter(neighbourId.toString());
+
+        if (nbr != null) {
+            log.debug("OspfChannelHandler::processDdMessage:: OSPFNeighborState {}", nbr.getState());
+            // set options for the NBR
+            nbr.setIsOpaqueCapable(ddPacket.isOpaqueCapable());
+            if (ddPacket.imtu() > mtu()) {
+                log.debug("the MTU size is greater than the interface MTU");
+                return;
+            }
+            if (nbr.getState() == OspfNeighborState.DOWN) {
+                return;
+            }
+            if (nbr.getState() == OspfNeighborState.ATTEMPT) {
+                return;
+            }
+            if (nbr.getState() == OspfNeighborState.TWOWAY) {
+                nbr.adjOk(channel);
+                return;
+            }
+            //if init is the state call twoWayReceived
+            if (nbr.getState() == OspfNeighborState.INIT) {
+                ((OspfNbrImpl) nbr).twoWayReceived(ddPacket, ctx.getChannel());
+            } else if (nbr.getState() == OspfNeighborState.EXSTART) {
+                //get I,M,MS Bits
+                int initialize = ddPacket.isInitialize();
+                int more = ddPacket.isMore();
+                int masterOrSlave = ddPacket.isMaster();
+                int options = ddPacket.options();
+                nbr.setOptions(options);
+
+                if (initialize == OspfUtil.INITIALIZE_SET && more == OspfUtil.MORE_SET &&
+                        masterOrSlave == OspfUtil.IS_MASTER) {
+                    if (ddPacket.getLsaHeaderList().isEmpty()) {
+                        if (OspfUtil.ipAddressToLong(ddPacket.routerId().toString()) >
+                                OspfUtil.ipAddressToLong(ospfArea.routerId().toString())) {
+                            nbr.setIsMaster(OspfUtil.IS_MASTER);
+                            ((OspfNbrImpl) nbr).setLastDdPacket(ddPacket);
+                            nbr.setDdSeqNum(ddPacket.sequenceNo());
+                            nbr.setOptions(ddPacket.options());
+                            ((OspfNbrImpl) nbr).negotiationDone(ddPacket, true, ddPacket.getLsaHeaderList(),
+                                                                ctx.getChannel());
+                        }
+                    }
+                }
+                if (initialize == OspfUtil.INITIALIZE_NOTSET && masterOrSlave == OspfUtil.NOT_MASTER) {
+                    if (nbr.ddSeqNum() == ddPacket.sequenceNo()) {
+                        if (OspfUtil.ipAddressToLong(ddPacket.routerId().toString()) <
+                                OspfUtil.ipAddressToLong(ospfArea.routerId().toString())) {
+                            ((OspfNbrImpl) nbr).setLastDdPacket(ddPacket);
+                            nbr.setOptions(ddPacket.options());
+                            nbr.setDdSeqNum(nbr.ddSeqNum() + 1);
+                            ((OspfNbrImpl) nbr).negotiationDone(ddPacket, false, ddPacket.getLsaHeaderList(),
+                                                                ctx.getChannel());
+                        }
+                    }
+                }
+
+            } else if (nbr.getState() == OspfNeighborState.EXCHANGE) {
+                //get I,M,MS Bits
+                log.debug("Neighbor state:: EXCHANGE");
+                boolean isDuplicateDDPacket = compareDdPackets(ddPacket, ((OspfNbrImpl) nbr).lastDdPacket());
+                int initialize = ddPacket.isInitialize();
+                int more = ddPacket.isMore();
+                int masterOrSlave = ddPacket.isMaster();
+                int options = ddPacket.options();
+
+                if (!isDuplicateDDPacket) {
+                    //if dd packet is not duplicate  then continue
+                    if (nbr.isMaster() != masterOrSlave) {
+                        DdPacket newResPacket =
+                                (DdPacket) ((OspfNbrImpl) nbr).seqNumMismatch("Master/Slave Inconsistency");
+                        newResPacket.setDestinationIp(ddPacket.sourceIp());
+                        log.debug("Sending back DDPacket to {}", ddPacket.sourceIp());
+                        byte[] messageToWrite = getMessage(newResPacket);
+                        ctx.getChannel().write(messageToWrite);
+                    } else if (initialize == 1) {
+                        DdPacket newResPacket =
+                                (DdPacket) ((OspfNbrImpl) nbr).seqNumMismatch("Initialize bit inconsistency");
+                        newResPacket.setDestinationIp(ddPacket.sourceIp());
+                        log.debug("Sending back DDPacket to {}", ddPacket.sourceIp());
+                        byte[] messageToWrite = getMessage(newResPacket);
+                        ctx.getChannel().write(messageToWrite);
+                    } else {
+
+                        if (masterOrSlave == OspfUtil.NOT_MASTER) {
+                            if (ddPacket.sequenceNo() == nbr.ddSeqNum()) {
+                                //Process the DD Packet
+                                ((OspfNbrImpl) nbr).processDdPacket(false, ddPacket, ctx.getChannel());
+                                log.debug("Received DD Packet");
+                            } else {
+                                DdPacket newResPacket =
+                                        (DdPacket) ((OspfNbrImpl) nbr).seqNumMismatch("Sequence Number Mismatch");
+                                newResPacket.setDestinationIp(ddPacket.sourceIp());
+                                log.debug("Sending back DDPacket to {}", ddPacket.sourceIp());
+                                byte[] messageToWrite = getMessage(newResPacket);
+                                ctx.getChannel().write(messageToWrite);
+                            }
+                        } else {
+                            //we are the slave
+                            if (ddPacket.sequenceNo() == (nbr.ddSeqNum() + 1)) {
+                                ((OspfNbrImpl) nbr).setLastDdPacket(ddPacket);
+                                ((OspfNbrImpl) nbr).processDdPacket(true, ddPacket, ctx.getChannel());
+                                log.debug("Process DD Packet");
+                            } else {
+                                DdPacket newResPacket =
+                                        (DdPacket) ((OspfNbrImpl) nbr).seqNumMismatch("options inconsistency");
+                                newResPacket.setDestinationIp(ddPacket.sourceIp());
+                                log.debug("Sending back DDPacket to {}", ddPacket.sourceIp());
+                                byte[] messageToWrite = getMessage(newResPacket);
+                                ctx.getChannel().write(messageToWrite);
+                            }
+                        }
+                    }
+                } else {
+                    if (masterOrSlave == OspfUtil.NOT_MASTER) {
+                        return;
+                    } else {
+                        DdPacket newResPacket = ((OspfNbrImpl) nbr).lastSentDdPacket();
+                        log.debug("Sending back DDPacket to {}", ddPacket.sourceIp());
+                        byte[] messageToWrite = getMessage(newResPacket);
+                        ctx.getChannel().write(messageToWrite);
+                    }
+                }
+            } else if (nbr.getState() == OspfNeighborState.LOADING || nbr.getState() == OspfNeighborState.FULL) {
+                //In case if we are slave then we have to send the last received DD Packet
+                int options = ddPacket.options();
+                if (nbr.options() != options) {
+                    OspfMessage newResPacket = ((OspfNbrImpl) nbr).seqNumMismatch("Initialize bit inconsistency");
+                    newResPacket.setDestinationIp(ddPacket.sourceIp());
+                    byte[] messageToWrite = getMessage(newResPacket);
+                    ctx.getChannel().write(messageToWrite);
+                } else if (ddPacket.isInitialize() == OspfUtil.INITIALIZE_SET) {
+                    OspfMessage newResPacket = ((OspfNbrImpl) nbr).seqNumMismatch("Initialize bit inconsistency");
+                    newResPacket.setDestinationIp(ddPacket.sourceIp());
+                    byte[] messageToWrite = getMessage(newResPacket);
+                    ctx.getChannel().write(messageToWrite);
+                }
+                boolean isDuplicate = compareDdPackets(ddPacket, ((OspfNbrImpl) nbr).lastDdPacket());
+                //we are master
+                if (nbr.isMaster() != OspfUtil.IS_MASTER) {
+                    // check if the packet is duplicate, duplicates should be discarded by the master
+                    if (isDuplicate) {
+                        log.debug("received a duplicate DD packet");
+                    }
+                } else {
+                    //The slave must respond to duplicates by repeating the last Database Description packet
+                    //that it had sent.
+                    if (isDuplicate) {
+                        ddPacket.setDestinationIp(ddPacket.sourceIp());
+                        byte[] messageToWrite = getMessage(((OspfNbrImpl) nbr).lastSentDdPacket());
+                        ctx.getChannel().write(messageToWrite);
+                        log.debug("Sending back the duplicate packet ");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Process the Ls Request message.
+     *
+     * @param ospfMessage OSPF message instance.
+     * @param ctx         channel handler context instance.
+     * @throws Exception might throws exception
+     */
+    void processLsRequestMessage(OspfMessage ospfMessage, ChannelHandlerContext ctx) throws Exception {
+        log.debug("OspfChannelHandler::processLsRequestMessage...!!!");
+        Channel channel = ctx.getChannel();
+        LsRequest lsrPacket = (LsRequest) ospfMessage;
+        OspfNbr nbr = neighbouringRouter(lsrPacket.routerId().toString());
+
+        if (nbr.getState() == OspfNeighborState.EXCHANGE || nbr.getState() == OspfNeighborState.LOADING ||
+                nbr.getState() == OspfNeighborState.FULL) {
+
+            LsRequest reqMsg = (LsRequest) ospfMessage;
+            if (reqMsg.getLinkStateRequests().isEmpty()) {
+                log.debug("Received Link State Request Vector is Empty ");
+                return;
+            } else {
+                //Send the LsUpdate back
+                ListIterator<LsRequestPacket> listItr = reqMsg.getLinkStateRequests().listIterator();
+                while (listItr.hasNext()) {
+                    LsUpdate lsupdate = new LsUpdate();
+                    lsupdate.setOspfVer(OspfUtil.OSPF_VERSION);
+                    lsupdate.setOspftype(OspfPacketType.LSUPDATE.value());
+                    lsupdate.setRouterId(ospfArea.routerId());
+                    lsupdate.setAreaId(ospfArea.areaId());
+                    lsupdate.setAuthType(OspfUtil.NOT_ASSIGNED);
+                    lsupdate.setAuthentication(OspfUtil.NOT_ASSIGNED);
+                    lsupdate.setOspfPacLength(OspfUtil.NOT_ASSIGNED); // to calculate packet length
+                    lsupdate.setChecksum(OspfUtil.NOT_ASSIGNED);
+
+                    //limit to mtu
+                    int currentLength = OspfUtil.OSPF_HEADER_LENGTH + OspfUtil.FOUR_BYTES;
+                    int maxSize = mtu() -
+                            OspfUtil.LSA_HEADER_LENGTH; // subtract a normal IP header.
+                    int noLsa = 0;
+                    while (listItr.hasNext()) {
+                        LsRequestPacket lsRequest = (LsRequestPacket) listItr.next();
+                        // to verify length of the LSA
+                        LsaWrapper wrapper = ospfArea.getLsa(lsRequest.lsType(), lsRequest.linkStateId(),
+                                                             lsRequest.ownRouterId());
+                        OspfLsa ospflsa = wrapper.ospfLsa();
+                        if ((currentLength + ((LsaWrapperImpl) wrapper).lsaHeader().lsPacketLen()) >= maxSize) {
+                            listItr.previous();
+                            break;
+                        }
+                        if (ospflsa != null) {
+                            lsupdate.addLsa(ospflsa);
+                            noLsa++;
+
+                            currentLength = currentLength + ((LsaWrapperImpl) wrapper).lsaHeader().lsPacketLen();
+                        } else {
+                            nbr.badLSReq(channel);
+                        }
+                    }
+                    lsupdate.setNumberOfLsa(noLsa);
+                    //set the destination
+                    if (state() == OspfInterfaceState.DR ||
+                            state() == OspfInterfaceState.BDR ||
+                            state() == OspfInterfaceState.POINT2POINT) {
+                        lsupdate.setDestinationIp(OspfUtil.ALL_SPF_ROUTERS);
+                    } else if (state() == OspfInterfaceState.DROTHER) {
+                        lsupdate.setDestinationIp(OspfUtil.ALL_DROUTERS);
+                    }
+                    byte[] messageToWrite = getMessage(lsupdate);
+                    ctx.getChannel().write(messageToWrite);
+                }
+            }
+        }
+    }
+
+    /**
+     * Process the ls update message.
+     *
+     * @param ospfMessage OSPF message instance.
+     * @param ctx         channel handler context instance.
+     * @throws Exception might throws exception
+     */
+    void processLsUpdateMessage(OspfMessage ospfMessage, ChannelHandlerContext ctx) throws Exception {
+        log.debug("OspfChannelHandler::processLsUpdateMessage");
+        LsUpdate lsUpdate = (LsUpdate) ospfMessage;
+        String neighbourId = lsUpdate.routerId().toString();
+        //LSUpdate packet has been associated with a particular neighbor.
+        //Neighbor should not be in lesser state than Exchange.
+        if (isNeighborInList(neighbourId)) {
+            OspfNbrImpl nbr = (OspfNbrImpl) neighbouringRouter(neighbourId);
+            if (nbr.getState() == OspfNeighborState.EXCHANGE ||
+                    nbr.getState() == OspfNeighborState.LOADING) {
+                nbr.processLsUpdate(lsUpdate, ctx.getChannel());
+            } else if (nbr.getState() == OspfNeighborState.FULL) {
+                if (lsUpdate.noLsa() != 0) {
+                    List<OspfLsa> list = lsUpdate.getLsaList();
+                    Iterator itr = list.iterator();
+                    while (itr.hasNext()) {
+                        LsaHeader lsa = (LsaHeader) itr.next();
+                        nbr.processReceivedLsa(lsa, true, ctx.getChannel(), lsUpdate.sourceIp());
+                    }
+                } else {
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * Process the ls acknowledge message.
+     *
+     * @param ospfMessage OSPF message instance.
+     * @param ctx         channel handler context instance.
+     * @throws Exception might throws exception
+     */
+    void processLsAckMessage(OspfMessage ospfMessage, ChannelHandlerContext ctx) throws Exception {
+        log.debug("OspfChannelHandler::processLsAckMessage");
+        LsAcknowledge lsAckPacket = (LsAcknowledge) ospfMessage;
+        //check it is present in listOfNeighbors
+        OspfNbrImpl nbr = (OspfNbrImpl) neighbouringRouter(lsAckPacket.routerId().toString());
+        if (nbr != null) {
+            if (nbr.getState().getValue() < OspfNeighborState.EXCHANGE.getValue()) {
+                // discard the packet.
+                return;
+            } else {
+                // process ls acknowledgements
+                Iterator itr = lsAckPacket.getLinkStateHeaders().iterator();
+                while (itr.hasNext()) {
+                    LsaHeader lsRequest = (LsaHeader) itr.next();
+
+                    OspfLsa ospfLsa =
+                            (OspfLsa) nbr.getPendingReTxList().get(((OspfAreaImpl) ospfArea).getLsaKey(lsRequest));
+                    if (lsRequest != null && ospfLsa != null) {
+                        String isSame = ((OspfLsdbImpl) ospfArea.database()).isNewerOrSameLsa(
+                                lsRequest, (LsaHeader) ospfLsa);
+                        if (isSame.equals("same")) {
+                            nbr.getPendingReTxList().remove(((OspfAreaImpl) ospfArea).getLsaKey(lsRequest));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Compares two Dd Packets to check whether its duplicate or not.
+     *
+     * @param receivedDPacket received DD packet from network.
+     * @param lastDdPacket    Last DdPacket which we sent.
+     * @return true if it is a duplicate packet else false.
+     */
+    public boolean compareDdPackets(DdPacket receivedDPacket, DdPacket lastDdPacket) {
+        if (receivedDPacket.isInitialize() == lastDdPacket.isInitialize()) {
+            if (receivedDPacket.isMaster() == lastDdPacket.isMaster()) {
+                if (receivedDPacket.isMore() == lastDdPacket.isMore()) {
+                    if (receivedDPacket.options() == lastDdPacket.options()) {
+                        if (receivedDPacket.sequenceNo() == lastDdPacket.sequenceNo()) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Starts the hello timer which sends hello packet every configured seconds.
+     */
+    public void startHelloTimer() {
+        log.debug("OSPFInterfaceChannelHandler::startHelloTimer");
+        exServiceHello = Executors.newSingleThreadScheduledExecutor();
+        helloTimerTask = new InternalHelloTimer();
+        final ScheduledFuture<?> helloHandle =
+                exServiceHello.scheduleAtFixedRate(helloTimerTask, delay, helloIntervalTime, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Stops the hello timer.
+     */
+    public void stopHelloTimer() {
+        log.debug("OSPFInterfaceChannelHandler::stopHelloTimer ");
+        exServiceHello.shutdown();
+    }
+
+    /**
+     * Starts the wait timer.
+     */
+    public void startWaitTimer() {
+        log.debug("OSPFNbr::startWaitTimer");
+        exServiceWait = Executors.newSingleThreadScheduledExecutor();
+        waitTimerTask = new InternalWaitTimer();
+        final ScheduledFuture<?> waitTimerHandle =
+                exServiceWait.schedule(waitTimerTask, routerDeadIntervalTime(), TimeUnit.SECONDS);
+    }
+
+    /**
+     * Stops the wait timer.
+     */
+    public void stopWaitTimer() {
+        log.debug("OSPFNbr::stopWaitTimer ");
+        exServiceWait.shutdown();
+    }
+
+    /**
+     * Starts the timer which waits for configured seconds and sends Delayed Ack Packet.
+     */
+    public void startDelayedAckTimer() {
+        if (!isDelayedAckTimerScheduled) {
+            log.debug("Started DelayedAckTimer...!!!");
+            exServiceDelayedAck = Executors.newSingleThreadScheduledExecutor();
+            delayedAckTimerTask = new InternalDelayedAckTimer();
+            final ScheduledFuture<?> delayAckHandle =
+                    exServiceDelayedAck.scheduleAtFixedRate(delayedAckTimerTask, delayedAckTimerInterval,
+                                                            delayedAckTimerInterval, TimeUnit.MILLISECONDS);
+            isDelayedAckTimerScheduled = true;
+        }
+    }
+
+    /**
+     * Stops the delayed acknowledge timer.
+     */
+    public void stopDelayedAckTimer() {
+        if (isDelayedAckTimerScheduled) {
+            log.debug("Stopped DelayedAckTimer...!!!");
+            isDelayedAckTimerScheduled = false;
+            exServiceDelayedAck.shutdown();
+        }
+    }
+
+    /**
+     * Performs DR election.
+     *
+     * @param ch Netty Channel instance.
+     * @throws Exception might throws exception
+     */
+    public void electRouter(Channel ch) throws Exception {
+
+        Ip4Address currentDr = dr();
+        Ip4Address currentBdr = bdr();
+        OspfInterfaceState oldState = state();
+        OspfInterfaceState newState;
+
+        log.debug("OSPFInterfaceChannelHandler::electRouter -> currentDr: {}, currentBdr: {}",
+                  currentDr, currentBdr);
+        List<OspfEligibleRouter> eligibleRouters = calculateListOfEligibleRouters(new OspfEligibleRouter());
+
+        log.debug("OSPFInterfaceChannelHandler::electRouter -> eligibleRouters: {}", eligibleRouters);
+        OspfEligibleRouter electedBdr = electBdr(eligibleRouters);
+        OspfEligibleRouter electedDr = electDr(eligibleRouters, electedBdr);
+
+        setBdr(electedBdr.getIpAddress());
+        setDr(electedDr.getIpAddress());
+
+        if (electedBdr.getIpAddress().equals(ipAddress()) &&
+                !electedBdr.getIpAddress().equals(currentBdr)) {
+            setState(OspfInterfaceState.BDR);
+        }
+
+        if (electedDr.getIpAddress().equals(ipAddress()) &&
+                !electedDr.getIpAddress().equals(currentDr)) {
+            setState(OspfInterfaceState.DR);
+        }
+
+        if (state() != oldState &&
+                !(state() == OspfInterfaceState.DROTHER &&
+                        oldState.value() < OspfInterfaceState.DROTHER.value())) {
+            log.debug("Recalculating as the State is changed ");
+            log.debug("OSPFInterfaceChannelHandler::electRouter -> currentDr: {}, currentBdr: {}",
+                      currentDr, currentBdr);
+            eligibleRouters = calculateListOfEligibleRouters(new OspfEligibleRouter());
+
+            log.debug("OSPFInterfaceChannelHandler::electRouter -> eligibleRouters: {}", eligibleRouters);
+            electedBdr = electBdr(eligibleRouters);
+            electedDr = electDr(eligibleRouters, electedBdr);
+
+            setBdr(electedBdr.getIpAddress());
+            setDr(electedDr.getIpAddress());
+        }
+
+        if (electedBdr.getIpAddress().equals(ipAddress()) &&
+                !electedBdr.getIpAddress().equals(currentBdr)) {
+            setState(OspfInterfaceState.BDR);
+            ospfArea.refreshArea(this);
+        }
+
+        if (electedDr.getIpAddress().equals(ipAddress()) &&
+                !electedDr.getIpAddress().equals(currentDr)) {
+            setState(OspfInterfaceState.DR);
+            //Refresh Router Lsa & Network Lsa
+            ospfArea.refreshArea(this);
+        }
+
+        if (currentDr != electedDr.getIpAddress() || currentBdr != electedBdr.getIpAddress()) {
+            Set<String> negibhorIdList;
+            negibhorIdList = listOfNeighbors().keySet();
+            for (String routerid : negibhorIdList) {
+                OspfNbrImpl nbr = (OspfNbrImpl) neighbouringRouter(routerid);
+                if (nbr.getState().getValue() >= OspfNeighborState.TWOWAY.getValue()) {
+                    nbr.adjOk(ch);
+                }
+            }
+        }
+
+        log.debug("OSPFInterfaceChannelHandler::electRouter -> ElectedDR: {}, ElectedBDR: {}",
+                  electedDr.getIpAddress(), electedBdr.getIpAddress());
+    }
+
+
+    /**
+     * BDR Election process. Find the list of eligible router to participate in the process.
+     *
+     * @param electedDr router elected as DR.
+     * @return list of eligible routers
+     */
+    public List<OspfEligibleRouter> calculateListOfEligibleRouters(OspfEligibleRouter electedDr) {
+        log.debug("OSPFNbr::calculateListOfEligibleRouters ");
+        Set<String> neighborIdList;
+        List<OspfEligibleRouter> eligibleRouters = new ArrayList<>();
+
+        neighborIdList = listOfNeighbors().keySet();
+        for (String routerId : neighborIdList) {
+            OspfNbrImpl nbr = (OspfNbrImpl) neighbouringRouter(routerId);
+            if (nbr.getState().getValue() >= OspfNeighborState.TWOWAY.getValue() &&
+                    nbr.routerPriority() > 0) {
+                OspfEligibleRouter router = new OspfEligibleRouter();
+                router.setIpAddress(nbr.neighborIpAddr());
+                router.setRouterId(nbr.neighborId());
+                router.setRouterPriority(nbr.routerPriority());
+                if (nbr.neighborDr().equals(nbr.neighborIpAddr()) ||
+                        electedDr.getIpAddress().equals(nbr.neighborIpAddr())) {
+                    router.setIsDr(true);
+                } else if (nbr.neighborBdr().equals(nbr.neighborIpAddr())) {
+                    router.setIsBdr(true);
+                }
+                eligibleRouters.add(router);
+            }
+        }
+        // interface does not have states like two and all
+        if (routerPriority() > 0) {
+            OspfEligibleRouter router = new OspfEligibleRouter();
+            router.setIpAddress(ipAddress());
+            router.setRouterId(ospfArea.routerId());
+            router.setRouterPriority(routerPriority());
+            if (dr().equals(ipAddress()) ||
+                    electedDr.getIpAddress().equals(ipAddress())) {
+                router.setIsDr(true);
+            } else if (bdr().equals(ipAddress()) &&
+                    !dr().equals(ipAddress())) {
+                router.setIsBdr(true);
+            }
+
+            eligibleRouters.add(router);
+        }
+
+        return eligibleRouters;
+    }
+
+    /**
+     * Based on router priority assigns BDR.
+     *
+     * @param eligibleRouters list of routers to participate in bdr election.
+     * @return OSPF Eligible router instance.
+     */
+    public OspfEligibleRouter electBdr(List<OspfEligibleRouter> eligibleRouters) {
+        log.debug("OSPFInterfaceChannelHandler::electBdr -> eligibleRouters: {}", eligibleRouters);
+        List<OspfEligibleRouter> declaredAsBdr = new ArrayList<>();
+        List<OspfEligibleRouter> notDrAndBdr = new ArrayList<>();
+        for (OspfEligibleRouter router : eligibleRouters) {
+            if (router.isBdr()) {
+                declaredAsBdr.add(router);
+            }
+            if (!router.isBdr() && !router.isDr()) {
+                notDrAndBdr.add(router);
+            }
+        }
+
+        OspfEligibleRouter electedBdr = new OspfEligibleRouter();
+        if (!declaredAsBdr.isEmpty()) {
+            if (declaredAsBdr.size() == 1) {
+                electedBdr = declaredAsBdr.get(0);
+            } else if (declaredAsBdr.size() > 1) {
+                electedBdr = selectRouterBasedOnPriority(declaredAsBdr);
+            }
+        } else {
+            if (notDrAndBdr.size() == 1) {
+                electedBdr = notDrAndBdr.get(0);
+            } else if (notDrAndBdr.size() > 1) {
+                electedBdr = selectRouterBasedOnPriority(notDrAndBdr);
+            }
+        }
+
+        electedBdr.setIsBdr(true);
+        electedBdr.setIsDr(false);
+
+        return electedBdr;
+    }
+
+    /**
+     * DR Election process.
+     *
+     * @param eligibleRouters list of eligible routers.
+     * @param electedBdr      Elected Bdr, OSPF eligible router instance.
+     * @return OSPF eligible router instance.
+     */
+    public OspfEligibleRouter electDr(List<OspfEligibleRouter> eligibleRouters,
+                                      OspfEligibleRouter electedBdr) {
+
+        List<OspfEligibleRouter> declaredAsDr = new ArrayList<>();
+        for (OspfEligibleRouter router : eligibleRouters) {
+            if (router.isDr()) {
+                declaredAsDr.add(router);
+            }
+        }
+
+        OspfEligibleRouter electedDr = new OspfEligibleRouter();
+        if (!declaredAsDr.isEmpty()) {
+            if (declaredAsDr.size() == 1) {
+                electedDr = declaredAsDr.get(0);
+            } else if (eligibleRouters.size() > 1) {
+                electedDr = selectRouterBasedOnPriority(declaredAsDr);
+            }
+        } else {
+            electedDr = electedBdr;
+            electedDr.setIsDr(true);
+            electedDr.setIsBdr(false);
+        }
+
+        return electedDr;
+    }
+
+    /**
+     * DR election process.
+     *
+     * @param routersList list of eligible routers.
+     * @return OSPF eligible router instance.
+     */
+    public OspfEligibleRouter selectRouterBasedOnPriority(List<OspfEligibleRouter> routersList) {
+
+        OspfEligibleRouter initialRouter = routersList.get(0);
+
+        for (int i = 1; i < routersList.size(); i++) {
+            OspfEligibleRouter router = routersList.get(i);
+            if (router.getRouterPriority() > initialRouter.getRouterPriority()) {
+                initialRouter = router;
+            } else if (router.getRouterPriority() == initialRouter.getRouterPriority()) {
+                try {
+                    if (OspfUtil.ipAddressToLong(router.getIpAddress().toString()) >
+                            OspfUtil.ipAddressToLong(initialRouter.getIpAddress().toString())) {
+                        initialRouter = router;
+                    }
+                } catch (Exception e) {
+                    log.debug("OSPFInterfaceChannelHandler::selectRouterBasedOnPriority ->" +
+                                      " eligibleRouters: {}", initialRouter);
+                }
+            }
+        }
+
+        return initialRouter;
+    }
+
+    /**
+     * Adds device information.
+     *
+     * @param ospfRouter OSPF router instance
+     */
+    public void addDeviceInformation(OspfRouter ospfRouter) {
+        controller.addDeviceDetails(ospfRouter);
+    }
+
+    /**
+     * removes device information.
+     *
+     * @param ospfRouter OSPF neighbor instance
+     */
+    public void removeDeviceInformation(OspfRouter ospfRouter) {
+        controller.removeDeviceDetails(ospfRouter);
+    }
+
+    /**
+     * Adds link information.
+     *
+     * @param ospfRouter  OSPF router instance
+     * @param ospfLinkTed list link ted instances
+     */
+    public void addLinkInformation(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) {
+        controller.addLinkDetails(ospfRouter, ospfLinkTed);
+    }
+
+    /**
+     * Removes link information.
+     *
+     * @param ospfRouter  OSPF router instance
+     * @param ospfLinkTed OSPF link TED instance
+     */
+    public void removeLinkInformation(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) {
+        controller.removeLinkDetails(ospfRouter, ospfLinkTed);
+    }
+
+    /**
+     * Gets message as bytes.
+     *
+     * @param ospfMessage OSPF message
+     * @return OSPF message
+     */
+    private byte[] getMessage(OspfMessage ospfMessage) {
+        OspfMessageWriter messageWriter = new OspfMessageWriter();
+        if (state().equals(OspfInterfaceState.POINT2POINT)) {
+            ospfMessage.setDestinationIp(OspfUtil.ALL_SPF_ROUTERS);
+        }
+        return (messageWriter.getMessage(ospfMessage, interfaceIndex, state.value()));
+    }
+
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -468,32 +1560,24 @@
             return false;
         }
         OspfInterfaceImpl that = (OspfInterfaceImpl) o;
-        return Objects.equal(areaId, that.areaId) &&
-                Objects.equal(helloIntervalTime, that.helloIntervalTime) &&
+        return Objects.equal(helloIntervalTime, that.helloIntervalTime) &&
                 Objects.equal(routerDeadIntervalTime, that.routerDeadIntervalTime) &&
-                Objects.equal(transmitDelay, that.transmitDelay) &&
                 Objects.equal(routerPriority, that.routerPriority) &&
-                Objects.equal(systemInterfaceType, that.systemInterfaceType) &&
                 Objects.equal(interfaceType, that.interfaceType) &&
-                Objects.equal(interfaceCost, that.interfaceCost) &&
-                Objects.equal(pollInterval, that.pollInterval) &&
                 Objects.equal(mtu, that.mtu) &&
                 Objects.equal(reTransmitInterval, that.reTransmitInterval) &&
                 Objects.equal(ipAddress, that.ipAddress) &&
                 Objects.equal(ipNetworkMask, that.ipNetworkMask) &&
                 Objects.equal(listOfNeighbors, that.listOfNeighbors) &&
-                Objects.equal(authType, that.authType) &&
-                Objects.equal(authKey, that.authKey) &&
                 Objects.equal(dr, that.dr) &&
                 Objects.equal(bdr, that.bdr);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(ipAddress, ipNetworkMask, areaId, helloIntervalTime,
-                                routerDeadIntervalTime, transmitDelay, routerPriority, listOfNeighbors,
-                                systemInterfaceType, interfaceType, interfaceCost, authType, authKey,
-                                pollInterval, mtu, reTransmitInterval, dr, bdr);
+        return Objects.hashCode(ipAddress, ipNetworkMask, helloIntervalTime,
+                                routerDeadIntervalTime, routerPriority, listOfNeighbors,
+                                interfaceType, mtu, reTransmitInterval, dr, bdr);
     }
 
     @Override
@@ -502,19 +1586,187 @@
                 .omitNullValues()
                 .add("ipAddress", ipAddress)
                 .add("routerPriority", routerPriority)
-                .add("areaID", areaId)
                 .add("helloIntervalTime", helloIntervalTime)
                 .add("routerDeadIntervalTime", routerDeadIntervalTime)
                 .add("interfaceType", interfaceType)
-                .add("interfaceCost", interfaceCost)
-                .add("authType", authType)
-                .add("authKey", authKey)
-                .add("pollInterval", pollInterval)
                 .add("mtu", mtu)
                 .add("reTransmitInterval", reTransmitInterval)
                 .add("dr", dr)
                 .add("bdr", bdr)
-                .add("transmitDelay", transmitDelay)
                 .toString();
     }
+
+    /**
+     * Represents a Hello task which sent a hello message every configured time interval.
+     */
+    private class InternalHelloTimer implements Runnable {
+
+        /**
+         * Creates an instance of Hello Timer.
+         */
+        InternalHelloTimer() {
+        }
+
+        @Override
+        public void run() {
+            if (channel != null && channel.isOpen() && channel.isConnected()) {
+                if (interfaceType() == OspfInterfaceType.BROADCAST.value()) {
+                    if (interfaceTypeOldValue != interfaceType()) {
+                        try {
+                            callDrElection(channel);
+                        } catch (Exception e) {
+                            log.debug("Error while calling interfaceUp {}", e.getMessage());
+                        }
+                    }
+                } else {
+                    if (interfaceTypeOldValue != interfaceType()) {
+                        interfaceTypeOldValue = interfaceType();
+                    }
+                }
+                HelloPacket hellopacket = new HelloPacket();
+                //Headers
+                hellopacket.setOspfVer(OspfUtil.OSPF_VERSION);
+                hellopacket.setOspftype(OspfPacketType.HELLO.value());
+                hellopacket.setOspfPacLength(0); //will be modified while encoding
+                hellopacket.setRouterId(ospfArea.routerId());
+                hellopacket.setAreaId(ospfArea.areaId());
+                hellopacket.setChecksum(0); //will be modified while encoding
+                hellopacket.setAuthType(OspfUtil.NOT_ASSIGNED);
+                hellopacket.setAuthentication(OspfUtil.NOT_ASSIGNED);
+                //Body
+                hellopacket.setNetworkMask(ipNetworkMask());
+                hellopacket.setOptions(ospfArea.options());
+                hellopacket.setHelloInterval(helloIntervalTime());
+                hellopacket.setRouterPriority(routerPriority());
+                hellopacket.setRouterDeadInterval(routerDeadIntervalTime());
+                hellopacket.setDr(dr());
+                hellopacket.setBdr(bdr());
+
+                Map<String, OspfNbr> listOfNeighbors = listOfNeighbors();
+                Set<String> keys = listOfNeighbors.keySet();
+                Iterator itr = keys.iterator();
+                while (itr.hasNext()) {
+                    String nbrKey = (String) itr.next();
+                    OspfNbrImpl nbr = (OspfNbrImpl) listOfNeighbors.get(nbrKey);
+                    if (nbr.getState() != OspfNeighborState.DOWN) {
+                        hellopacket.addNeighbor(Ip4Address.valueOf(nbrKey));
+                    }
+                }
+                // build a hello Packet
+                if (channel == null || !channel.isOpen() || !channel.isConnected()) {
+                    log.debug("Hello Packet not sent !!.. Channel Issue...");
+                    return;
+                }
+
+                hellopacket.setDestinationIp(OspfUtil.ALL_SPF_ROUTERS);
+                byte[] messageToWrite = getMessage(hellopacket);
+                ChannelFuture future = channel.write(messageToWrite);
+                if (future.isSuccess()) {
+                    log.debug("Hello Packet successfully sent !!");
+                } else {
+                    future.awaitUninterruptibly();
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Represents a Wait Timer task which waits the interface state to become WAITING.
+     * It initiates DR election process.
+     */
+    private class InternalWaitTimer implements Runnable {
+        Channel ch;
+
+        /**
+         * Creates an instance of Wait Timer.
+         */
+        InternalWaitTimer() {
+            this.ch = channel;
+        }
+
+        @Override
+        public void run() {
+            log.debug("Wait timer expires...");
+            if (ch != null && ch.isConnected()) {
+                try {
+                    waitTimer(ch);
+                } catch (Exception e) {
+                    log.debug("Exception at wait timer ...!!!");
+                }
+            }
+        }
+    }
+
+    /**
+     * Represents a task which sent a LS Acknowledge from the link state headers list.
+     */
+    private class InternalDelayedAckTimer implements Runnable {
+        Channel ch;
+
+        /**
+         * Creates an instance of Delayed acknowledge timer.
+         */
+        InternalDelayedAckTimer() {
+            this.ch = channel;
+        }
+
+        @Override
+        public void run() {
+            if (!linkStateHeaders().isEmpty()) {
+                isDelayedAckTimerScheduled = true;
+                if (ch != null && ch.isConnected()) {
+
+                    List<LsaHeader> listOfLsaHeadersAcknowledged = new ArrayList<>();
+                    List<LsaHeader> listOfLsaHeaders = linkStateHeaders();
+                    log.debug("Delayed Ack, Number of Lsa's to Ack {}", listOfLsaHeaders.size());
+                    Iterator itr = listOfLsaHeaders.iterator();
+                    while (itr.hasNext()) {
+                        LsAcknowledge ackContent = new LsAcknowledge();
+                        //Setting OSPF Header
+                        ackContent.setOspfVer(OspfUtil.OSPF_VERSION);
+                        ackContent.setOspftype(OspfPacketType.LSAACK.value());
+                        ackContent.setRouterId(ospfArea.routerId());
+                        ackContent.setAreaId(ospfArea.areaId());
+                        ackContent.setAuthType(OspfUtil.NOT_ASSIGNED);
+                        ackContent.setAuthentication(OspfUtil.NOT_ASSIGNED);
+                        ackContent.setOspfPacLength(OspfUtil.NOT_ASSIGNED);
+                        ackContent.setChecksum(OspfUtil.NOT_ASSIGNED);
+                        //limit to mtu
+                        int currentLength = OspfUtil.OSPF_HEADER_LENGTH;
+                        int maxSize = mtu() -
+                                OspfUtil.LSA_HEADER_LENGTH; // subtract a normal IP header.
+                        while (itr.hasNext()) {
+                            if ((currentLength + OspfUtil.LSA_HEADER_LENGTH) >= maxSize) {
+                                break;
+                            }
+                            LsaHeader lsaHeader = (LsaHeader) itr.next();
+                            ackContent.addLinkStateHeader(lsaHeader);
+                            currentLength = currentLength + OspfUtil.LSA_HEADER_LENGTH;
+                            listOfLsaHeadersAcknowledged.add(lsaHeader);
+                            log.debug("Delayed Ack, Added Lsa's to Ack {}", lsaHeader);
+                        }
+
+                        log.debug("Delayed Ack, Number of Lsa's in LsAck packet {}",
+                                  ackContent.getLinkStateHeaders().size());
+
+                        //set the destination
+                        if (state() == OspfInterfaceState.DR || state() == OspfInterfaceState.BDR
+                                || state() == OspfInterfaceState.POINT2POINT) {
+                            ackContent.setDestinationIp(OspfUtil.ALL_SPF_ROUTERS);
+                        } else if (state() == OspfInterfaceState.DROTHER) {
+                            ackContent.setDestinationIp(OspfUtil.ALL_DROUTERS);
+                        }
+                        byte[] messageToWrite = getMessage(ackContent);
+                        ch.write(messageToWrite);
+
+                        for (LsaHeader lsa : listOfLsaHeadersAcknowledged) {
+                            linkStateHeaders().remove(lsa);
+                            removeLsaFromNeighborMap(((OspfAreaImpl) ospfArea).getLsaKey(lsa));
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
\ No newline at end of file