Refactor Ipv6Service module.
[netvirt.git] / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / IfMgr.java
index 0243bcd49cdccc0bdd395aa0213850fc043fbfc3..b62b182281c2140ab48c7e4321bf914d84441f89 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.netvirt.ipv6service;
 
+import com.google.common.collect.Lists;
 import com.google.common.net.InetAddresses;
 import io.netty.util.Timeout;
 import java.math.BigInteger;
@@ -16,6 +17,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
@@ -25,6 +27,8 @@ import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.ipv6util.api.Ipv6Constants.Ipv6RouterAdvertisementType;
+import org.opendaylight.genius.ipv6util.api.Ipv6Util;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
 import org.opendaylight.netvirt.elanmanager.api.IElanService;
@@ -33,33 +37,28 @@ import org.opendaylight.netvirt.ipv6service.api.IVirtualNetwork;
 import org.opendaylight.netvirt.ipv6service.api.IVirtualPort;
 import org.opendaylight.netvirt.ipv6service.api.IVirtualRouter;
 import org.opendaylight.netvirt.ipv6service.api.IVirtualSubnet;
-import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
-import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants.Ipv6RtrAdvertType;
 import org.opendaylight.netvirt.ipv6service.utils.Ipv6PeriodicTrQueue;
+import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceConstants;
 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
 import org.opendaylight.netvirt.ipv6service.utils.Ipv6TimerWheel;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -74,8 +73,11 @@ public class IfMgr implements ElementCache, AutoCloseable {
     private final ConcurrentMap<Uuid, VirtualSubnet> vsubnets = new ConcurrentHashMap<>();
     private final ConcurrentMap<Uuid, VirtualPort> vintfs = new ConcurrentHashMap<>();
     private final ConcurrentMap<Uuid, VirtualPort> vrouterv6IntfMap = new ConcurrentHashMap<>();
-    private final ConcurrentMap<Uuid, List<VirtualPort>> unprocessedRouterIntfs = new ConcurrentHashMap<>();
-    private final ConcurrentMap<Uuid, List<VirtualPort>> unprocessedSubnetIntfs = new ConcurrentHashMap<>();
+    private final ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedRouterIntfs = new ConcurrentHashMap<>();
+    private final ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedSubnetIntfs = new ConcurrentHashMap<>();
+    private static ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedNetIntfs = new ConcurrentHashMap<>();
+    private static ConcurrentMap<Uuid, Integer> unprocessedNetRSFlowIntfs = new ConcurrentHashMap<>();
+    private static ConcurrentMap<Uuid, Set<Ipv6Address>> unprocessedNetNSFlowIntfs = new ConcurrentHashMap<>();
     private final OdlInterfaceRpcService interfaceManagerRpc;
     private final IElanService elanProvider;
     private final Ipv6ServiceUtils ipv6ServiceUtils;
@@ -87,8 +89,8 @@ public class IfMgr implements ElementCache, AutoCloseable {
 
     @Inject
     public IfMgr(DataBroker dataBroker, IElanService elanProvider, OdlInterfaceRpcService interfaceManagerRpc,
-            PacketProcessingService packetService, Ipv6ServiceUtils ipv6ServiceUtils,
-            Ipv6ServiceEosHandler ipv6ServiceEosHandler) {
+                 PacketProcessingService packetService, Ipv6ServiceUtils ipv6ServiceUtils,
+                 Ipv6ServiceEosHandler ipv6ServiceEosHandler) {
         this.dataBroker = dataBroker;
         this.elanProvider = elanProvider;
         this.interfaceManagerRpc = interfaceManagerRpc;
@@ -116,7 +118,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
         VirtualRouter rtr = VirtualRouter.builder().routerUUID(rtrUuid).tenantID(tenantId).name(rtrName).build();
         vrouters.put(rtrUuid, rtr);
 
-        List<VirtualPort> intfList = unprocessedRouterIntfs.remove(rtrUuid);
+        Set<VirtualPort> intfList = unprocessedRouterIntfs.remove(rtrUuid);
         if (intfList == null) {
             LOG.debug("No unprocessed interfaces for the router {}", rtrUuid);
             return;
@@ -182,7 +184,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
 
         vsubnets.put(snetId, snet);
 
-        List<VirtualPort> intfList = unprocessedSubnetIntfs.remove(snetId);
+        Set<VirtualPort> intfList = unprocessedSubnetIntfs.remove(snetId);
         if (intfList == null) {
             LOG.debug("No unprocessed interfaces for the subnet {}", snetId);
             return;
@@ -198,6 +200,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
                     if (rtr != null) {
                         rtr.addSubnet(snet);
                     }
+                    updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
                 }
             }
         }
@@ -225,7 +228,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
                               Uuid networkId, IpAddress fixedIp, String macAddress,
                               String deviceOwner) {
         LOG.debug("addRouterIntf portId {}, rtrId {}, snetId {}, networkId {}, ip {}, mac {}",
-            portId, rtrId, snetId, networkId, fixedIp, macAddress);
+                portId, rtrId, snetId, networkId, fixedIp, macAddress);
         //Save the interface ipv6 address in its fully expanded format
         Ipv6Address addr = new Ipv6Address(InetAddresses
                 .forString(fixedIp.getIpv6Address().getValue()).getHostAddress());
@@ -241,14 +244,15 @@ public class IfMgr implements ElementCache, AutoCloseable {
         if (prevIntf == null) {
             newIntf = true;
             MacAddress ifaceMac = MacAddress.getDefaultInstance(macAddress);
-            Ipv6Address llAddr = ipv6ServiceUtils.getIpv6LinkLocalAddressFromMac(ifaceMac);
+            Ipv6Address llAddr = Ipv6Util.getIpv6LinkLocalAddressFromMac(ifaceMac);
             /* A new router interface is created. This is basically triggered when an
             IPv6 subnet is associated to the router. Check if network is already hosting
             any VMs. If so, on all the hosts that have VMs on the network, program the
             icmpv6 punt flows in IPV6_TABLE(45).
              */
-            programIcmpv6RSPuntFlows(intf, Ipv6Constants.ADD_FLOW);
-            programIcmpv6NSPuntFlowForAddress(intf, llAddr, Ipv6Constants.ADD_FLOW);
+            programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6ServiceConstants.ADD_FLOW);
+            programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), llAddr, Ipv6ServiceConstants.ADD_FLOW);
+            programIcmpv6NaForwardFlows(intf, snetId, Ipv6ServiceConstants.ADD_FLOW);
         } else {
             intf = prevIntf;
             intf.setSubnetInfo(snetId, fixedIp);
@@ -273,7 +277,8 @@ public class IfMgr implements ElementCache, AutoCloseable {
             vrouterv6IntfMap.put(networkId, intf);
         }
 
-        programIcmpv6NSPuntFlowForAddress(intf, fixedIp.getIpv6Address(), Ipv6Constants.ADD_FLOW);
+        programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), fixedIp.getIpv6Address(), Ipv6ServiceConstants.ADD_FLOW);
+        programIcmpv6NaPuntFlow(networkId, intf.getSubnets(), Ipv6ServiceConstants.ADD_FLOW);
 
         if (newIntf) {
             LOG.debug("start the periodic RA Timer for routerIntf {}", portId);
@@ -281,16 +286,14 @@ public class IfMgr implements ElementCache, AutoCloseable {
         }
     }
 
-    public void updateRouterIntf(Uuid portId, Uuid rtrId, List<FixedIps> fixedIpsList) {
+    public void updateRouterIntf(Uuid portId, Uuid rtrId, List<FixedIps> fixedIpsList, Set<FixedIps> deletedIps) {
         LOG.info("updateRouterIntf portId {}, fixedIpsList {} ", portId, fixedIpsList);
         VirtualPort intf = getPort(portId);
         if (intf == null) {
             LOG.info("Skip Router interface update for non-ipv6 port {}", portId);
             return;
         }
-
-        List<Ipv6Address> existingIPv6AddressList = intf.getIpv6AddressesWithoutLLA();
-        List<Ipv6Address> newlyAddedIpv6AddressList = new ArrayList<>();
+        Uuid networkID = intf.getNetworkID();
         intf.clearSubnetInfo();
         for (FixedIps fip : fixedIpsList) {
             IpAddress fixedIp = fip.getIpAddress();
@@ -321,31 +324,21 @@ public class IfMgr implements ElementCache, AutoCloseable {
                 addUnprocessed(unprocessedSubnetIntfs, subnetId, intf);
             }
 
-
-            Uuid networkID = intf.getNetworkID();
             if (networkID != null) {
                 vrouterv6IntfMap.put(networkID, intf);
             }
-
-            if (existingIPv6AddressList.contains(fixedIp.getIpv6Address())) {
-                existingIPv6AddressList.remove(fixedIp.getIpv6Address());
-            } else {
-                newlyAddedIpv6AddressList.add(fixedIp.getIpv6Address());
-            }
         }
 
-        /* This is a port update event for routerPort. Check if any IPv6 subnet is added
-         or removed from the router port. Depending on subnet added/removed, we add/remove
-         the corresponding flows from IPV6_TABLE(45).
+        /*
+         * This is a port update event for routerPort. Check if any IPv6 subnet is added or removed from the
+         * router port. Depending on subnet added/removed, we add/remove the corresponding flows from
+         * IPV6_TABLE(45). Add is handled in addInterfaceInfo(), delete case is handled here.
          */
-        for (Ipv6Address ipv6Address: newlyAddedIpv6AddressList) {
-            // Some v6 subnets are associated to the routerPort add the corresponding NS Flows.
-            programIcmpv6NSPuntFlowForAddress(intf, ipv6Address, Ipv6Constants.ADD_FLOW);
-        }
-
-        for (Ipv6Address ipv6Address: existingIPv6AddressList) {
-            // Some v6 subnets are disassociated from the routerPort, remove the corresponding NS Flows.
-            programIcmpv6NSPuntFlowForAddress(intf, ipv6Address, Ipv6Constants.DEL_FLOW);
+        for (FixedIps ips : deletedIps) {
+            VirtualSubnet snet = getSubnet(ips.getSubnetId());
+            programIcmpv6NaPuntFlow(networkID, Lists.newArrayList(snet), Ipv6ServiceConstants.DEL_FLOW);
+            programIcmpv6NSPuntFlowForAddress(networkID, ips.getIpAddress().getIpv6Address(),
+                    Ipv6ServiceConstants.DEL_FLOW);
         }
     }
 
@@ -356,7 +349,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
     public void addHostIntf(Uuid portId, Uuid snetId, Uuid networkId,
                             IpAddress fixedIp, String macAddress, String deviceOwner) {
         LOG.debug("addHostIntf portId {}, snetId {}, networkId {}, ip {}, mac {}",
-            portId, snetId, networkId, fixedIp, macAddress);
+                portId, snetId, networkId, fixedIp, macAddress);
 
         //Save the interface ipv6 address in its fully expanded format
         Ipv6Address addr = new Ipv6Address(InetAddresses
@@ -429,7 +422,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
 
     public void updateDpnInfo(Uuid portId, BigInteger dpId, Long ofPort) {
         LOG.info("In updateDpnInfo portId {}, dpId {}, ofPort {}",
-            portId, dpId, ofPort);
+                portId, dpId, ofPort);
         VirtualPort intf = getPort(portId);
         if (intf != null) {
             intf.setDpId(dpId);
@@ -438,11 +431,19 @@ public class IfMgr implements ElementCache, AutoCloseable {
             // Update the network <--> List[dpnIds, List<ports>] cache.
             VirtualNetwork vnet = getNetwork(intf.getNetworkID());
             if (null != vnet) {
-                vnet.updateDpnPortInfo(dpId, ofPort, Ipv6Constants.ADD_ENTRY);
+                vnet.updateDpnPortInfo(dpId, ofPort, Ipv6ServiceConstants.ADD_ENTRY);
+            } else {
+                LOG.error("In updateDpnInfo networks NOT FOUND: networkID {}, portId {}, dpId {}, ofPort {}",
+                           intf.getNetworkID(), portId, dpId, ofPort);
+                addUnprocessed(unprocessedNetIntfs, intf.getNetworkID(), intf);
             }
+        } else {
+            LOG.error("In updateDpnInfo port NOT FOUND: portId {}, dpId {}, ofPort {}",
+                    portId, dpId, ofPort);
         }
     }
 
+
     public void updateInterfaceDpidOfPortInfo(Uuid portId) {
         LOG.debug("In updateInterfaceDpidOfPortInfo portId {}", portId);
         Interface interfaceState = ipv6ServiceUtils.getInterfaceStateFromOperDS(portId.getValue());
@@ -453,8 +454,8 @@ public class IfMgr implements ElementCache, AutoCloseable {
 
         List<String> ofportIds = interfaceState.getLowerLayerIf();
         NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
-        BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
-        if (!dpId.equals(Ipv6Constants.INVALID_DPID)) {
+        BigInteger dpId = ipv6ServiceUtils.getDpIdFromInterfaceState(interfaceState);
+        if (!dpId.equals(Ipv6ServiceConstants.INVALID_DPID)) {
             Long ofPort = MDSALUtil.getOfPortNumberFromPortName(nodeConnectorId);
             updateDpnInfo(portId, dpId, ofPort);
         }
@@ -466,7 +467,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
         if (intf != null) {
             intf.removeSelf();
             Uuid networkID = intf.getNetworkID();
-            if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6Constants.NETWORK_ROUTER_INTERFACE)) {
+            if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6ServiceConstants.NETWORK_ROUTER_INTERFACE)) {
                 LOG.info("In removePort for router interface, portId {}", portId);
 
                 if (networkID != null) {
@@ -476,11 +477,15 @@ public class IfMgr implements ElementCache, AutoCloseable {
                 /* Router port is deleted. Remove the corresponding icmpv6 punt flows on all
                 the dpnIds which were hosting the VMs on the network.
                  */
-                programIcmpv6RSPuntFlows(intf, Ipv6Constants.DEL_FLOW);
+                programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6ServiceConstants.DEL_FLOW);
+                programIcmpv6NaPuntFlow(networkID, intf.getSubnets(), Ipv6ServiceConstants.DEL_FLOW);
                 for (Ipv6Address ipv6Address: intf.getIpv6Addresses()) {
-                    programIcmpv6NSPuntFlowForAddress(intf, ipv6Address, Ipv6Constants.DEL_FLOW);
+                    programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), ipv6Address, Ipv6ServiceConstants.DEL_FLOW);
+                }
+                for (VirtualSubnet subnet : intf.getSubnets()) {
+                    programIcmpv6NaForwardFlows(intf, subnet.getSubnetUUID(), Ipv6ServiceConstants.DEL_FLOW);
                 }
-                transmitRouterAdvertisement(intf, Ipv6RtrAdvertType.CEASE_ADVERTISEMENT);
+                transmitRouterAdvertisement(intf, Ipv6RouterAdvertisementType.CEASE_ADVERTISEMENT);
                 timer.cancelPeriodicTransmissionTimeout(intf.getPeriodicTimeout());
                 intf.resetPeriodicTimeout();
                 LOG.debug("Reset the periodic RA Timer for intf {}", intf.getIntfUUID());
@@ -492,7 +497,7 @@ public class IfMgr implements ElementCache, AutoCloseable {
                 VirtualNetwork vnet = getNetwork(networkID);
                 if (null != vnet) {
                     BigInteger dpId = intf.getDpId();
-                    vnet.updateDpnPortInfo(dpId, intf.getOfPort(), Ipv6Constants.DEL_ENTRY);
+                    vnet.updateDpnPortInfo(dpId, intf.getOfPort(), Ipv6ServiceConstants.DEL_ENTRY);
                 }
             }
         }
@@ -502,16 +507,49 @@ public class IfMgr implements ElementCache, AutoCloseable {
         // Nothing to do for now
     }
 
-    public void addUnprocessed(Map<Uuid, List<VirtualPort>> unprocessed, Uuid id, VirtualPort intf) {
+    public void addUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id, VirtualPort intf) {
         if (id != null) {
-            unprocessed.computeIfAbsent(id, key -> Collections.synchronizedList(new ArrayList<>())).add(intf);
+            unprocessed.computeIfAbsent(id,
+                key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet())).add(intf);
         }
     }
 
-    public void removeUnprocessed(Map<Uuid, List<VirtualPort>> unprocessed, Uuid id) {
+    public Set<VirtualPort> removeUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id) {
         if (id != null) {
-            unprocessed.remove(id);
+            return unprocessed.remove(id);
+        }
+        return null;
+    }
+
+    public void addUnprocessedRSFlows(Map<Uuid, Integer>
+                                              unprocessed, Uuid id, Integer action) {
+        unprocessed.put(id, action);
+
+    }
+
+    public Integer removeUnprocessedRSFlows(Map<Uuid, Integer>
+                                                    unprocessed, Uuid id) {
+        return unprocessed.remove(id);
+    }
+
+    public void addUnprocessedNSFlows(Map<Uuid, Set<Ipv6Address>> unprocessed,
+                                      Uuid id, Ipv6Address ipv6Address,
+                                      Integer action) {
+        Set<Ipv6Address> ipv6AddressesList = unprocessed.get(id);
+        if (action == Ipv6ServiceConstants.ADD_FLOW) {
+            unprocessed.computeIfAbsent(id, key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet()))
+                    .add(ipv6Address);
+        } else if (action == Ipv6ServiceConstants.DEL_FLOW) {
+            if ((ipv6AddressesList != null) && (ipv6AddressesList.contains(ipv6Address))) {
+                ipv6AddressesList.remove(ipv6Address);
+            }
         }
+        return;
+    }
+
+    public Set<Ipv6Address> removeUnprocessedNSFlows(Map<Uuid, Set<Ipv6Address>>
+                                                             unprocessed, Uuid id) {
+        return unprocessed.remove(id);
     }
 
     public VirtualPort getRouterV6InterfaceForNetwork(Uuid networkId) {
@@ -525,93 +563,193 @@ public class IfMgr implements ElementCache, AutoCloseable {
             return null;
         }
         for (VirtualSubnet snet : intf.getSubnets()) {
-            if (snet.getIpVersion().equals(Ipv6Constants.IP_VERSION_V6)) {
+            if (snet.getIpVersion().equals(Ipv6ServiceConstants.IP_VERSION_V6)) {
                 return intf;
             }
         }
         return null;
     }
 
-    private void programIcmpv6RSPuntFlows(IVirtualPort routerPort, int action) {
+    private void programIcmpv6RSPuntFlows(Uuid networkId, int action) {
         if (!ipv6ServiceEosHandler.isClusterOwner()) {
             LOG.trace("Not a cluster Owner, skip flow programming.");
             return;
         }
 
-        Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
+        Long elanTag = getNetworkElanTag(networkId);
         int flowStatus;
-        VirtualNetwork vnet = getNetwork(routerPort.getNetworkID());
+        VirtualNetwork vnet = getNetwork(networkId);
         if (vnet != null) {
             List<BigInteger> dpnList = vnet.getDpnsHostingNetwork();
             for (BigInteger dpId : dpnList) {
                 flowStatus = vnet.getRSPuntFlowStatusOnDpnId(dpId);
-                if (action == Ipv6Constants.ADD_FLOW && flowStatus == Ipv6Constants.FLOWS_NOT_CONFIGURED) {
+                if (action == Ipv6ServiceConstants.ADD_FLOW
+                        && flowStatus == Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED) {
                     ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
-                            Ipv6Constants.ADD_FLOW);
-                    vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6Constants.FLOWS_CONFIGURED);
-                } else if (action == Ipv6Constants.DEL_FLOW && flowStatus == Ipv6Constants.FLOWS_CONFIGURED) {
+                            Ipv6ServiceConstants.ADD_FLOW);
+                    vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_CONFIGURED);
+                } else if (action == Ipv6ServiceConstants.DEL_FLOW
+                        && flowStatus == Ipv6ServiceConstants.FLOWS_CONFIGURED) {
                     ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
-                            Ipv6Constants.DEL_FLOW);
-                    vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6Constants.FLOWS_NOT_CONFIGURED);
+                            Ipv6ServiceConstants.DEL_FLOW);
+                    vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED);
                 }
             }
+        } else {
+            addUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId, action);
         }
     }
 
-    private void programIcmpv6NSPuntFlowForAddress(IVirtualPort routerPort, Ipv6Address ipv6Address, int action) {
+    private void programIcmpv6NSPuntFlowForAddress(Uuid networkId, Ipv6Address ipv6Address, int action) {
         if (!ipv6ServiceEosHandler.isClusterOwner()) {
             LOG.trace("Not a cluster Owner, skip flow programming.");
             return;
         }
 
-        Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
-        VirtualNetwork vnet = getNetwork(routerPort.getNetworkID());
+        Long elanTag = getNetworkElanTag(networkId);
+        VirtualNetwork vnet = getNetwork(networkId);
         if (vnet != null) {
             Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
             for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
-                if (action == Ipv6Constants.ADD_FLOW && !dpnIfaceInfo.ndTargetFlowsPunted.contains(ipv6Address)
+                if (action == Ipv6ServiceConstants.ADD_FLOW
+                        && !dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)
                         && dpnIfaceInfo.getDpId() != null) {
-                    ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(),
-                            elanTag, ipv6Address.getValue(), Ipv6Constants.ADD_FLOW);
+                    ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
+                            ipv6Address.getValue(), Ipv6ServiceConstants.ADD_FLOW);
                     dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
-                } else if (action == Ipv6Constants.DEL_FLOW && dpnIfaceInfo.ndTargetFlowsPunted.contains(ipv6Address)) {
-                    ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(),
-                            elanTag, ipv6Address.getValue(), Ipv6Constants.DEL_FLOW);
+                } else if (action == Ipv6ServiceConstants.DEL_FLOW
+                        && dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
+                    ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
+                            ipv6Address.getValue(), Ipv6ServiceConstants.DEL_FLOW);
                     dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
                 }
             }
+        } else {
+            addUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId, ipv6Address, action);
         }
     }
 
-    public void programIcmpv6PuntFlowsIfNecessary(Uuid vmPortId, BigInteger dpId, VirtualPort routerPort) {
+    private void programIcmpv6NaPuntFlow(Uuid networkID, List<VirtualSubnet> subnets, int action) {
         if (!ipv6ServiceEosHandler.isClusterOwner()) {
             LOG.trace("Not a cluster Owner, skip flow programming.");
             return;
         }
+        Long elanTag = getNetworkElanTag(networkID);
+        VirtualNetwork vnet = getNetwork(networkID);
+        if (vnet != null) {
+            Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
+            for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
+                if (dpnIfaceInfo.getDpId() == null) {
+                    continue;
+                }
+                for (VirtualSubnet subnet : subnets) {
+                    Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
+                    if (ipv6SubnetPrefix != null) {
+                        if (action == Ipv6ServiceConstants.ADD_FLOW
+                                && !dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
+                            ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
+                                    dpnIfaceInfo.getDpId(), elanTag, action);
+                            dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
+                        } else if (action == Ipv6ServiceConstants.DEL_FLOW
+                                && dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
+                            ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
+                                    dpnIfaceInfo.getDpId(), elanTag, action);
+                            dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
+                        }
+                    }
+                }
+            }
+        }
+    }
 
-        IVirtualPort vmPort = getPort(vmPortId);
-        if (null != vmPort) {
-            VirtualNetwork vnet = getNetwork(vmPort.getNetworkID());
-            if (null != vnet) {
-                VirtualNetwork.DpnInterfaceInfo dpnInfo = vnet.getDpnIfaceInfo(dpId);
-                if (null != dpnInfo) {
-                    Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
-                    if (vnet.getRSPuntFlowStatusOnDpnId(dpId) == Ipv6Constants.FLOWS_NOT_CONFIGURED) {
-                        ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
-                                Ipv6Constants.ADD_FLOW);
-                        vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6Constants.FLOWS_CONFIGURED);
+    public void handleInterfaceStateEvent(IVirtualPort port, BigInteger dpId, VirtualPort routerPort, int addOrRemove) {
+        Long elanTag = getNetworkElanTag(port.getNetworkID());
+        if (addOrRemove == Ipv6ServiceConstants.ADD_FLOW && routerPort != null) {
+            // Check and program icmpv6 punt flows on the dpnID if its the first VM on the host.
+            programIcmpv6PuntFlows(port, dpId, elanTag, routerPort);
+        }
+        programIcmpv6NaForwardFlow(port, dpId, elanTag, addOrRemove);
+    }
+
+    private void programIcmpv6PuntFlows(IVirtualPort vmPort, BigInteger dpId, Long elanTag, VirtualPort routerPort) {
+        VirtualNetwork vnet = getNetwork(vmPort.getNetworkID());
+        if (null != vnet) {
+            VirtualNetwork.DpnInterfaceInfo dpnInfo = vnet.getDpnIfaceInfo(dpId);
+            if (null != dpnInfo) {
+                if (vnet.getRSPuntFlowStatusOnDpnId(dpId) == Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED) {
+                    ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
+                            Ipv6ServiceConstants.ADD_FLOW);
+                    vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_CONFIGURED);
+                }
+
+                for (Ipv6Address ipv6Address : routerPort.getIpv6Addresses()) {
+                    if (!dpnInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
+                        ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpId,
+                                elanTag, ipv6Address.getValue(), Ipv6ServiceConstants.ADD_FLOW);
+                        dpnInfo.updateNDTargetAddress(ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
                     }
+                }
 
-                    for (Ipv6Address ipv6Address: routerPort.getIpv6Addresses()) {
-                        if (!dpnInfo.ndTargetFlowsPunted.contains(ipv6Address)) {
-                            ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpId,
-                                    elanTag, ipv6Address.getValue(), Ipv6Constants.ADD_FLOW);
-                            dpnInfo.updateNDTargetAddress(ipv6Address, Ipv6Constants.ADD_FLOW);
-                        }
+                for (VirtualSubnet subnet : routerPort.getSubnets()) {
+                    Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
+                    if (ipv6SubnetPrefix != null
+                            && !dpnInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
+                        ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix, dpId,
+                                elanTag, Ipv6ServiceConstants.ADD_FLOW);
+                        dpnInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), Ipv6ServiceConstants.ADD_FLOW);
+                    }
+                }
+            }
+        }
+    }
+
+    private void programIcmpv6NaForwardFlows(IVirtualPort routerPort, Uuid snetId, int action) {
+        if (!ipv6ServiceEosHandler.isClusterOwner()) {
+            LOG.trace("Not a cluster Owner, skip flow programming.");
+            return;
+        }
+
+        VirtualNetwork vnet = getNetwork(routerPort.getNetworkID());
+        if (vnet != null) {
+            Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
+            Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
+            for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
+                if (dpnIfaceInfo.getDpId() == null) {
+                    continue;
+                }
+                List<VirtualPort> vmPorts = getVmPortsInSubnetByDpId(snetId, dpnIfaceInfo.getDpId());
+                for (VirtualPort vmPort : vmPorts) {
+                    programIcmpv6NaForwardFlow(vmPort, dpnIfaceInfo.getDpId(), elanTag, action);
+                }
+            }
+        }
+    }
+
+    /**
+     * Programs ICMPv6 NA forwarding flow for fixed IPs of neutron port. NA's from non-fixed IPs are
+     * punted to controller for learning.
+     *
+     * @param vmPort the VM port
+     * @param dpId the DP ID
+     * @param elanTag the ELAN tag
+     * @param addOrRemove the add or remove flag
+     */
+    private void programIcmpv6NaForwardFlow(IVirtualPort vmPort, BigInteger dpId, Long elanTag, int addOrRemove) {
+        ipv6ServiceUtils.installIcmpv6NaForwardFlow(NwConstants.IPV6_TABLE, vmPort, dpId, elanTag, addOrRemove);
+    }
+
+    public List<VirtualPort> getVmPortsInSubnetByDpId(Uuid snetId, BigInteger dpId) {
+        List<VirtualPort> vmPorts = new ArrayList<>();
+        for (VirtualPort port : vintfs.values()) {
+            if (dpId.equals(port.getDpId()) && Ipv6ServiceUtils.isVmPort(port.getDeviceOwner())) {
+                for (VirtualSubnet subnet : port.getSubnets()) {
+                    if (subnet.getSubnetUUID().equals(snetId)) {
+                        vmPorts.add(port);
                     }
                 }
             }
         }
+        return vmPorts;
     }
 
     public String getInterfaceNameFromTag(long portTag) {
@@ -669,44 +807,84 @@ public class IfMgr implements ElementCache, AutoCloseable {
         if (vnetworks.putIfAbsent(networkId, new VirtualNetwork(networkId)) == null) {
             updateNetworkElanTag(networkId);
         }
+        Set<VirtualPort> intfList = removeUnprocessed(unprocessedNetIntfs, networkId);
+        if (intfList == null) {
+            LOG.info("No unprocessed interfaces for the net {}", networkId);
+            return;
+        }
+
+        for (VirtualPort intf : intfList) {
+            if (intf != null) {
+                updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
+            }
+        }
+
+        Set<Ipv6Address> ipv6Addresses =
+                removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
+
+        for (Ipv6Address ipv6Address : ipv6Addresses) {
+            programIcmpv6NSPuntFlowForAddress(networkId, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
+        }
+
+        Integer action = removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
+        programIcmpv6RSPuntFlows(networkId, action);
     }
 
     public void removeNetwork(Uuid networkId) {
         // Delete the network and the corresponding dpnIds<-->List(ports) cache.
         VirtualNetwork net = networkId != null ? vnetworks.remove(networkId) : null;
-        if (null != net) {
+        if (null != net && null != net.getNetworkUuid()) {
+            /* removing all RS flows when network gets removed, as the DPN-list is maintained only as part of
+            * network cache. After removal of network there is no way to remove them today. */
+            programIcmpv6RSPuntFlows(net.getNetworkUuid(), Ipv6ServiceConstants.DEL_FLOW);
+            removeAllIcmpv6NSPuntFlowForNetwork(net.getNetworkUuid());
             net.removeSelf();
         }
+        removeUnprocessed(unprocessedNetIntfs, networkId);
+        removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
+        removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
     }
 
-    private void transmitRouterAdvertisement(VirtualPort intf, Ipv6RtrAdvertType advType) {
-        Ipv6RouterAdvt ipv6RouterAdvert = new Ipv6RouterAdvt(packetService);
+    private void transmitRouterAdvertisement(VirtualPort intf, Ipv6RouterAdvertisementType advType) {
+        Ipv6RouterAdvt ipv6RouterAdvert = new Ipv6RouterAdvt(packetService, this);
 
-        LOG.debug("in transmitRouterAdvertisement for {}", advType);
         VirtualNetwork vnet = getNetwork(intf.getNetworkID());
         if (vnet != null) {
-            String nodeName;
-            String outPort;
+            long elanTag = vnet.getElanTag();
             Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
             for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
-                nodeName = Ipv6Constants.OPENFLOW_NODE_PREFIX + dpnIfaceInfo.getDpId();
-                List<NodeConnectorRef> ncRefList = new ArrayList<>();
-                for (Long ofPort: dpnIfaceInfo.ofPortList) {
-                    outPort = nodeName + ":" + ofPort;
-                    LOG.debug("Transmitting RA {} for node {}, port {}", advType, nodeName, outPort);
-                    InstanceIdentifier<NodeConnector> outPortId = InstanceIdentifier.builder(Nodes.class)
-                            .child(Node.class, new NodeKey(new NodeId(nodeName)))
-                            .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(outPort)))
-                            .build();
-                    ncRefList.add(new NodeConnectorRef(outPortId));
+                LOG.debug("transmitRouterAdvertisement: Transmitting RA {} for ELAN Tag {}",
+                        advType, elanTag);
+                if (dpnIfaceInfo.getDpId() != null) {
+                    ipv6RouterAdvert.transmitRtrAdvertisement(advType, intf, elanTag, null,
+                            dpnIfaceInfo.getDpId(), intf.getIntfUUID());
                 }
-                if (!ncRefList.isEmpty()) {
-                    ipv6RouterAdvert.transmitRtrAdvertisement(advType, intf, ncRefList, null);
+            }
+        }
+    }
+
+
+    private void removeAllIcmpv6NSPuntFlowForNetwork(Uuid networkId) {
+        Long elanTag = getNetworkElanTag(networkId);
+        VirtualNetwork vnet = vnetworks.get(networkId);
+        if (vnet == null) {
+            return;
+        }
+
+        Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
+
+        for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
+            for (Ipv6Address ipv6Address : dpnIfaceInfo.ndTargetFlowsPunted) {
+                if (ipv6ServiceEosHandler.isClusterOwner()) {
+                    ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(),
+                            elanTag, ipv6Address.getValue(), Ipv6ServiceConstants.DEL_FLOW);
                 }
+                dpnIfaceInfo.updateNDTargetAddress(ipv6Address, Ipv6ServiceConstants.DEL_ENTRY);
             }
         }
     }
 
+
     public void transmitUnsolicitedRA(Uuid portId) {
         VirtualPort port = getPort(portId);
         LOG.debug("in transmitUnsolicitedRA for {}, port {}", portId, port);
@@ -720,14 +898,14 @@ public class IfMgr implements ElementCache, AutoCloseable {
             /* Only the Cluster Owner would be sending out the Periodic RAs.
                However, the timer is configured on all the nodes to handle cluster fail-over scenarios.
              */
-            transmitRouterAdvertisement(port, Ipv6RtrAdvertType.UNSOLICITED_ADVERTISEMENT);
+            transmitRouterAdvertisement(port, Ipv6RouterAdvertisementType.UNSOLICITED_ADVERTISEMENT);
         }
         Timeout portTimeout = timer.setPeriodicTransmissionTimeout(port.getPeriodicTimer(),
-                                                                   Ipv6Constants.PERIODIC_RA_INTERVAL,
-                                                                   TimeUnit.SECONDS);
+                Ipv6ServiceConstants.PERIODIC_RA_INTERVAL,
+                TimeUnit.SECONDS);
         port.setPeriodicTimeout(portTimeout);
         LOG.debug("re-started periodic RA Timer for routerIntf {}, int {}s", port.getIntfUUID(),
-                   Ipv6Constants.PERIODIC_RA_INTERVAL);
+                Ipv6ServiceConstants.PERIODIC_RA_INTERVAL);
     }
 
     @Override
@@ -765,4 +943,24 @@ public class IfMgr implements ElementCache, AutoCloseable {
         }
         return virtualRouter;
     }
+
+    public List<Action> getEgressAction(String interfaceName) {
+        List<Action> actions = null;
+        try {
+            GetEgressActionsForInterfaceInputBuilder egressAction =
+                    new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
+            Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
+                    interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
+            RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
+            if (!rpcResult.isSuccessful()) {
+                LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
+                        interfaceName, rpcResult.getErrors());
+            } else {
+                actions = rpcResult.getResult().getAction();
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
+        }
+        return actions;
+    }
 }