Switch to JDT annotations for Nullable and NonNull
[netvirt.git] / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / utils / Ipv6ServiceUtils.java
index fdcdf151eb49439cf884ad2bbe1ab18a5aa815af..17049240d7b61b7ffc8b37bd1840fb3cb0eac78c 100644 (file)
@@ -9,11 +9,9 @@
 package org.opendaylight.netvirt.ipv6service.utils;
 
 import com.google.common.base.Optional;
-import com.google.common.net.InetAddresses;
 import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -21,9 +19,16 @@ import java.util.concurrent.ExecutionException;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.infra.Datastore;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.genius.ipv6util.api.Icmpv6Type;
+import org.opendaylight.genius.ipv6util.api.Ipv6Constants;
+import org.opendaylight.genius.ipv6util.api.Ipv6Util;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
 import org.opendaylight.genius.mdsalutil.FlowEntity;
 import org.opendaylight.genius.mdsalutil.InstructionInfo;
@@ -31,6 +36,10 @@ import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.MatchInfo;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.NwConstants.NxmOfFieldType;
+import org.opendaylight.genius.mdsalutil.actions.ActionLearn;
+import org.opendaylight.genius.mdsalutil.actions.ActionLearn.FlowMod;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
@@ -38,14 +47,19 @@ import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv6;
 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6NdTarget;
+import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Source;
 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
+import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
 import org.opendaylight.genius.utils.ServiceIndex;
+import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
+import org.opendaylight.netvirt.ipv6service.VirtualSubnet;
+import org.opendaylight.netvirt.ipv6service.api.IVirtualPort;
 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;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
 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.opendaylight.flow.types.rev131026.instruction.list.Instruction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
@@ -57,8 +71,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.ser
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.config.rev181010.Ipv6serviceConfig;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
@@ -67,9 +81,10 @@ import org.slf4j.LoggerFactory;
 @Singleton
 public class Ipv6ServiceUtils {
     private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
-    public static final Ipv6Address ALL_NODES_MCAST_ADDR = newIpv6Address("FF02::1");
+    public static final Ipv6Address ALL_NODES_MCAST_ADDR = newIpv6Address(Ipv6Constants.ALL_NODES_MCAST_ADDRESS);
     public static final Ipv6Address UNSPECIFIED_ADDR = newIpv6Address("0:0:0:0:0:0:0:0");
 
+    @Nullable
     private static Ipv6Address newIpv6Address(String ip) {
         try {
             return Ipv6Address.getDefaultInstance(InetAddress.getByName(ip).getHostAddress());
@@ -81,11 +96,15 @@ public class Ipv6ServiceUtils {
 
     private final DataBroker broker;
     private final IMdsalApiManager mdsalUtil;
+    private final ManagedNewTransactionRunner txRunner;
+    private final Ipv6serviceConfig ipv6serviceConfig;
 
     @Inject
-    public Ipv6ServiceUtils(DataBroker broker, IMdsalApiManager mdsalUtil) {
+    public Ipv6ServiceUtils(DataBroker broker, IMdsalApiManager mdsalUtil, Ipv6serviceConfig ipv6ServiceConfig) {
         this.broker = broker;
         this.mdsalUtil = mdsalUtil;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
+        this.ipv6serviceConfig = ipv6ServiceConfig;
     }
 
     /**
@@ -108,14 +127,8 @@ public class Ipv6ServiceUtils {
      * @return the interface.
      */
     public org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
-        .Interface getInterface(String interfaceName) {
-        Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
-            .Interface> optInterface =
-                read(LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName));
-        if (optInterface.isPresent()) {
-            return optInterface.get();
-        }
-        return null;
+        .@Nullable Interface getInterface(String interfaceName) {
+        return read(LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName)).orNull();
     }
 
     /**
@@ -153,286 +166,199 @@ public class Ipv6ServiceUtils {
      * @return the interface state.
      */
     public org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
-            .Interface getInterfaceStateFromOperDS(String interfaceName) {
-        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
-                .interfaces.state.Interface> ifStateId = buildStateInterfaceId(interfaceName);
-        return MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, ifStateId, broker).orNull();
+            .@Nullable Interface getInterfaceStateFromOperDS(String interfaceName) {
+        return MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, buildStateInterfaceId(interfaceName), broker).orNull();
     }
 
-    public static String bytesToHexString(byte[] bytes) {
-        if (bytes == null) {
-            return "null";
-        }
-        StringBuilder buf = new StringBuilder();
-        for (int i = 0; i < bytes.length; i++) {
-            if (i > 0) {
-                buf.append(":");
-            }
-            short u8byte = (short) (bytes[i] & 0xff);
-            String tmp = Integer.toHexString(u8byte);
-            if (tmp.length() == 1) {
-                buf.append("0");
-            }
-            buf.append(tmp);
-        }
-        return buf.toString();
+    private static List<MatchInfo> getIcmpv6RSMatch(Long elanTag) {
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(MatchIpProtocol.ICMPV6);
+        matches.add(new MatchIcmpv6(Icmpv6Type.ROUTER_SOLICITATION.getValue(), (short) 0));
+        matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
+        return matches;
     }
 
-    public static byte[] bytesFromHexString(String values) {
-        String target = "";
-        if (values != null) {
-            target = values;
-        }
-        String[] octets = target.split(":");
-
-        byte[] ret = new byte[octets.length];
-        for (int i = 0; i < octets.length; i++) {
-            ret[i] = Integer.valueOf(octets[i], 16).byteValue();
-        }
-        return ret;
+    private List<MatchInfo> getIcmpv6NSMatch(Long elanTag, Ipv6Address ipv6Address) {
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(MatchIpProtocol.ICMPV6);
+        matches.add(new MatchIcmpv6(Icmpv6Type.NEIGHBOR_SOLICITATION.getValue(), (short) 0));
+        matches.add(new MatchIpv6NdTarget(ipv6Address));
+        matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
+        return matches;
     }
 
-    public static int calcIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
-        long checksum = getSummation(ip6Hdr.getSourceIpv6());
-        checksum += getSummation(ip6Hdr.getDestinationIpv6());
-        checksum = normalizeChecksum(checksum);
-
-        checksum += ip6Hdr.getIpv6Length();
-        checksum += ip6Hdr.getNextHeader();
-
-        int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
-        long value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
-        checksum += value;
-        checksum = normalizeChecksum(checksum);
-        icmp6Offset += 2;
-
-        //move to icmp6 payload skipping the checksum field
-        icmp6Offset += 2;
-        int length = packet.length - icmp6Offset;
-        while (length > 1) {
-            value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
-            checksum += value;
-            checksum = normalizeChecksum(checksum);
-            icmp6Offset += 2;
-            length -= 2;
-        }
-
-        if (length > 0) {
-            checksum += packet[icmp6Offset];
-            checksum = normalizeChecksum(checksum);
-        }
-
-        int finalChecksum = (int)(~checksum & 0xffff);
-        return finalChecksum;
+    private List<MatchInfo> getIcmpv6NAMatch(Long elanTag) {
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(MatchIpProtocol.ICMPV6);
+        matches.add(new MatchIcmpv6(Icmpv6Type.NEIGHBOR_ADVERTISEMENT.getValue(), (short) 0));
+        matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
+        return matches;
     }
 
-    public static boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
-        return calcIcmpv6Checksum(packet, ip6Hdr) == recvChecksum;
+    private static String getIPv6FlowRef(BigInteger dpId, Long elanTag, String flowType) {
+        return new StringBuffer().append(Ipv6ServiceConstants.FLOWID_PREFIX)
+                .append(dpId).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
+                .append(elanTag).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
+                .append(flowType).toString();
     }
 
-    private static long getSummation(Ipv6Address addr) {
-        byte[] baddr = null;
-        try {
-            baddr = InetAddress.getByName(addr.getValue()).getAddress();
-        } catch (UnknownHostException e) {
-            LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
-            return 0;
-        }
+    public void installIcmpv6NsPuntFlow(short tableId, BigInteger dpId, Long elanTag, Ipv6Address ipv6Address,
+            int addOrRemove) {
+        String flowId = getIPv6FlowRef(dpId, elanTag, Ipv6Util.getFormattedIpv6Address(ipv6Address));
 
-        long sum = 0;
-        int len = 0;
-        long value = 0;
-        while (len < baddr.length) {
-            value = (baddr[len] & 0xff) << 8 | baddr[len + 1] & 0xff;
-            sum += value;
-            sum = normalizeChecksum(sum);
-            len += 2;
-        }
-        return sum;
-    }
+        if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
+            LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
+            LoggingFutures
+                    .addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
+                        mdsalUtil.removeFlow(tx, dpId, flowId, tableId);
+                    }), LOG, "Error while removing flow={}", flowId);
+        } else {
+            List<ActionInfo> actionsInfos = new ArrayList<>();
+            actionsInfos.add(new ActionPuntToController());
 
-    private static  long normalizeChecksum(long value) {
-        if ((value & 0xffff0000) != 0) {
-            value = value & 0xffff;
-            value += 1;
+            int ndPuntTimeout = ipv6serviceConfig.getNeighborDiscoveryPuntTimeout();
+            if (isNdPuntProtectionEnabled(ndPuntTimeout)) {
+                actionsInfos.add(getLearnActionForNsPuntProtection(ndPuntTimeout));
+            }
+            List<InstructionInfo> instructions = Arrays.asList(new InstructionApplyActions(actionsInfos));
+            List<MatchInfo> nsMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
+            FlowEntity nsFlowEntity =
+                    MDSALUtil.buildFlowEntity(dpId, tableId, flowId, Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY,
+                            "IPv6NS", 0, 0, NwConstants.COOKIE_IPV6_TABLE, nsMatch, instructions);
+
+            LOG.trace("Installing IPv6 Neighbor Solicitation Flow DpId={}, elanTag={} ipv6Address={}", dpId, elanTag,
+                    ipv6Address.getValue());
+            LoggingFutures
+                    .addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
+                        mdsalUtil.addFlow(tx, nsFlowEntity);
+                    }), LOG, "Error while adding flow={}", nsFlowEntity);
         }
-        return value;
     }
 
-    public static byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
-        byte[] data = new byte[16];
-        Arrays.fill(data, (byte)0);
+    private ActionLearn getLearnActionForNsPuntProtection(int ndPuntTimeout) {
+        List<FlowMod> flowMods = getFlowModsForIpv6PuntProtection(Icmpv6Type.NEIGHBOR_SOLICITATION);
+        flowMods.add(new ActionLearn.MatchFromField(NxmOfFieldType.NXM_NX_ND_TARGET.getType(),
+                NxmOfFieldType.NXM_NX_ND_TARGET.getType(), NxmOfFieldType.NXM_NX_ND_TARGET.getFlowModHeaderLenInt()));
 
-        ByteBuffer buf = ByteBuffer.wrap(data);
-        buf.put(bytesFromHexString(ethPdu.getDestinationMac().getValue()));
-        buf.put(bytesFromHexString(ethPdu.getSourceMac().getValue()));
-        buf.putShort((short)ethPdu.getEthertype().intValue());
-        return data;
+        return new ActionLearn(0, ndPuntTimeout, Ipv6ServiceConstants.NS_PUNT_PROTECTION_FLOW_PRIORITY,
+                NwConstants.COOKIE_IPV6_TABLE, 0, NwConstants.IPV6_TABLE, 0, 0, flowMods);
     }
 
-    public static byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
-        byte[] data = new byte[128];
-        Arrays.fill(data, (byte)0);
-
-        ByteBuffer buf = ByteBuffer.wrap(data);
-        long flowLabel = (long)(ip6Pdu.getVersion() & 0x0f) << 28
-                | ip6Pdu.getFlowLabel() & 0x0fffffff;
-        buf.putInt((int)flowLabel);
-        buf.putShort((short)ip6Pdu.getIpv6Length().intValue());
-        buf.put((byte)ip6Pdu.getNextHeader().shortValue());
-        buf.put((byte)ip6Pdu.getHopLimit().shortValue());
-        try {
-            byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
-            buf.put(baddr);
-            baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
-            buf.put(baddr);
-        } catch (UnknownHostException e) {
-            LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
+    public void installIcmpv6RsPuntFlow(short tableId, BigInteger dpId, Long elanTag, int addOrRemove) {
+        if (dpId == null || dpId.equals(Ipv6ServiceConstants.INVALID_DPID)) {
+            return;
         }
-        return data;
-    }
-
-    public static Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
-        byte[] octets = bytesFromHexString(mac.getValue());
-
-        /* As per the RFC2373, steps involved to generate a LLA include
-           1. Convert the 48 bit MAC address to 64 bit value by inserting 0xFFFE
-              between OUI and NIC Specific part.
-           2. Invert the Universal/Local flag in the OUI portion of the address.
-           3. Use the prefix "FE80::/10" along with the above 64 bit Interface
-              identifier to generate the IPv6 LLA. */
-
-        StringBuilder interfaceID = new StringBuilder();
-        short u8byte = (short) (octets[0] & 0xff);
-        u8byte ^= 1 << 1;
-        interfaceID.append(Integer.toHexString(0xFF & u8byte));
-        interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
-        interfaceID.append(":");
-        interfaceID.append(Integer.toHexString(0xFF & octets[2]));
-        interfaceID.append("ff:fe");
-        interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
-        interfaceID.append(":");
-        interfaceID.append(Integer.toHexString(0xFF & octets[4]));
-        interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
-
-        // Return the address in its fully expanded format.
-        Ipv6Address ipv6LLA = new Ipv6Address(InetAddresses.forString(
-                "fe80:0:0:0:" + interfaceID.toString()).getHostAddress());
-        return ipv6LLA;
-    }
-
-    public static Ipv6Address getIpv6SolicitedNodeMcastAddress(Ipv6Address ipv6Address) {
+        String flowId = getIPv6FlowRef(dpId, elanTag, "IPv6RS");
+        if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
+            LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
+            LoggingFutures
+                    .addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
+                        mdsalUtil.removeFlow(tx, dpId, flowId, tableId);
+                    }), LOG, "Error while removing flow={}", flowId);
+        } else {
+            List<ActionInfo> actionsInfos = new ArrayList<>();
+            // Punt to controller
+            actionsInfos.add(new ActionPuntToController());
 
-        /* According to RFC 4291, a Solicited Node Multicast Address is derived by adding the 24
-           lower order bits with the Solicited Node multicast prefix (i.e., FF02::1:FF00:0/104).
-           Example: For IPv6Address of FE80::2AA:FF:FE28:9C5A, the corresponding solicited node
-           multicast address would be FF02::1:FF28:9C5A
-         */
+            int rdPuntTimeout = ipv6serviceConfig.getRouterDiscoveryPuntTimeout();
+            if (isRdPuntProtectionEnabled(rdPuntTimeout)) {
+                actionsInfos.add(getLearnActionForRsPuntProtection(rdPuntTimeout));
+            }
+            List<InstructionInfo> instructions = Arrays.asList(new InstructionApplyActions(actionsInfos));
+            List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(elanTag);
+            FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
+                    flowId,Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
+                    NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
 
-        byte[] octets;
-        try {
-            octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
-        } catch (UnknownHostException e) {
-            LOG.error("getIpv6SolicitedNodeMcastAddress: Failed to serialize ipv6Address ", e);
-            return null;
+            LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
+            LoggingFutures
+                    .addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
+                        mdsalUtil.addFlow(tx, rsFlowEntity);
+                    }), LOG, "Error while adding flow={}", rsFlowEntity);
         }
-
-        // Return the address in its fully expanded format.
-        Ipv6Address solictedV6Address = new Ipv6Address(InetAddresses.forString("ff02::1:ff"
-                 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[13]), 2, "0") + ":"
-                 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[14]), 2, "0")
-                 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[15]), 2, "0")).getHostAddress());
-
-        return solictedV6Address;
     }
 
-    public static MacAddress getIpv6MulticastMacAddress(Ipv6Address ipv6Address) {
-
-        /* According to RFC 2464, a Multicast MAC address is derived by concatenating 32 lower
-           order bits of IPv6 Multicast Address with the multicast prefix (i.e., 33:33).
-           Example: For Multicast IPv6Address of FF02::1:FF28:9C5A, the corresponding L2 multicast
-           address would be 33:33:28:9C:5A
-         */
-        byte[] octets;
-        try {
-            octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
-        } catch (UnknownHostException e) {
-            LOG.error("getIpv6MulticastMacAddress: Failed to serialize ipv6Address ", e);
-            return null;
-        }
-
-        String macAddress = "33:33:"
-                + StringUtils.leftPad(Integer.toHexString(0xFF & octets[12]), 2, "0") + ":"
-                + StringUtils.leftPad(Integer.toHexString(0xFF & octets[13]), 2, "0") + ":"
-                + StringUtils.leftPad(Integer.toHexString(0xFF & octets[14]), 2, "0") + ":"
-                + StringUtils.leftPad(Integer.toHexString(0xFF & octets[15]), 2, "0");
-
-        return new MacAddress(macAddress);
+    private ActionLearn getLearnActionForRsPuntProtection(int rdPuntTimeout) {
+        return new ActionLearn(0, rdPuntTimeout, Ipv6ServiceConstants.RS_PUNT_PROTECTION_FLOW_PRIORITY,
+                NwConstants.COOKIE_IPV6_TABLE, 0, NwConstants.IPV6_TABLE, 0, 0,
+                getFlowModsForIpv6PuntProtection(Icmpv6Type.ROUTER_SOLICITATION));
     }
 
-    private static List<MatchInfo> getIcmpv6RSMatch(Long elanTag) {
-        List<MatchInfo> matches = new ArrayList<>();
-        matches.add(MatchEthernetType.IPV6);
-        matches.add(MatchIpProtocol.ICMPV6);
-        matches.add(new MatchIcmpv6(Ipv6Constants.ICMP_V6_RS_CODE, (short) 0));
-        matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
-        return matches;
+    private List<FlowMod> getFlowModsForIpv6PuntProtection(Icmpv6Type icmpv6Type) {
+        return new ArrayList<>(Arrays.asList(
+                new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV6, NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
+                        NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
+                new ActionLearn.MatchFromValue(IPProtocols.IPV6ICMP.shortValue(),
+                        NxmOfFieldType.NXM_OF_IP_PROTO.getType(),
+                        NxmOfFieldType.NXM_OF_IP_PROTO.getFlowModHeaderLenInt()),
+                new ActionLearn.MatchFromValue(icmpv6Type.getValue(), NxmOfFieldType.NXM_OF_ICMPv6_TYPE.getType(),
+                        NxmOfFieldType.NXM_OF_ICMPv6_TYPE.getFlowModHeaderLenInt()),
+                new ActionLearn.MatchFromField(NxmOfFieldType.NXM_OF_ICMPv6_CODE.getType(),
+                        NxmOfFieldType.NXM_OF_ICMPv6_CODE.getType(),
+                        NxmOfFieldType.NXM_OF_ICMPv6_CODE.getFlowModHeaderLenInt()),
+                new ActionLearn.MatchFromField(NxmOfFieldType.OXM_OF_METADATA.getType(),
+                        MetaDataUtil.METADATA_LPORT_TAG_OFFSET, NxmOfFieldType.OXM_OF_METADATA.getType(),
+                        MetaDataUtil.METADATA_LPORT_TAG_OFFSET, MetaDataUtil.METADATA_LPORT_TAG_BITLEN)));
     }
 
-    private List<MatchInfo> getIcmpv6NSMatch(Long elanTag, String ndTarget) {
-        List<MatchInfo> matches = new ArrayList<>();
-        matches.add(MatchEthernetType.IPV6);
-        matches.add(MatchIpProtocol.ICMPV6);
-        matches.add(new MatchIcmpv6(Ipv6Constants.ICMP_V6_NS_CODE, (short) 0));
-        matches.add(new MatchIpv6NdTarget(new Ipv6Address(ndTarget)));
-        matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
-        return matches;
+    private boolean isRdPuntProtectionEnabled(int rdPuntTimeout) {
+        return rdPuntTimeout != 0;
     }
 
-    private static String getIPv6FlowRef(BigInteger dpId, Long elanTag, String flowType) {
-        return new StringBuffer().append(Ipv6Constants.FLOWID_PREFIX)
-                .append(dpId).append(Ipv6Constants.FLOWID_SEPARATOR)
-                .append(elanTag).append(Ipv6Constants.FLOWID_SEPARATOR)
-                .append(flowType).toString();
+    private boolean isNdPuntProtectionEnabled(int ndPuntTimeout) {
+        return ndPuntTimeout != 0;
     }
 
-    public void installIcmpv6NsPuntFlow(short tableId, BigInteger dpId,  Long elanTag, String ipv6Address,
+    public void installIcmpv6NaForwardFlow(short tableId, IVirtualPort vmPort, BigInteger dpId, Long elanTag,
             int addOrRemove) {
-        List<MatchInfo> neighborSolicitationMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
+        List<MatchInfo> matches = getIcmpv6NAMatch(elanTag);
         List<InstructionInfo> instructions = new ArrayList<>();
         List<ActionInfo> actionsInfos = new ArrayList<>();
-        actionsInfos.add(new ActionPuntToController());
+        actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
         instructions.add(new InstructionApplyActions(actionsInfos));
-        FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
-                getIPv6FlowRef(dpId, elanTag, ipv6Address),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NS",
-                0, 0, NwConstants.COOKIE_IPV6_TABLE, neighborSolicitationMatch, instructions);
-        if (addOrRemove == Ipv6Constants.DEL_FLOW) {
-            LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
-            mdsalUtil.removeFlow(rsFlowEntity);
-        } else {
-            LOG.trace("Installing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
-            mdsalUtil.installFlow(rsFlowEntity);
+
+        for (Ipv6Address ipv6Address : vmPort.getIpv6Addresses()) {
+            matches.add(new MatchIpv6Source(ipv6Address.getValue() + NwConstants.IPV6PREFIX));
+            String flowId = getIPv6FlowRef(dpId, elanTag,
+                    vmPort.getIntfUUID().getValue() + Ipv6ServiceConstants.FLOWID_SEPARATOR + ipv6Address.getValue());
+            FlowEntity rsFlowEntity =
+                    MDSALUtil.buildFlowEntity(dpId, tableId, flowId, Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY,
+                            "IPv6NA", 0, 0, NwConstants.COOKIE_IPV6_TABLE, matches, instructions);
+            if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
+                LOG.trace("Removing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}, ipv6Address {}", dpId,
+                        elanTag, ipv6Address.getValue());
+                mdsalUtil.removeFlow(rsFlowEntity);
+            } else {
+                LOG.trace("Installing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}, ipv6Address {}", dpId,
+                        elanTag, ipv6Address.getValue());
+                mdsalUtil.installFlow(rsFlowEntity);
+            }
         }
     }
 
-    public void installIcmpv6RsPuntFlow(short tableId, BigInteger dpId, Long elanTag, int addOrRemove) {
-        if (dpId == null || dpId.equals(Ipv6Constants.INVALID_DPID)) {
-            return;
-        }
-        List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(elanTag);
+    public void installIcmpv6NaPuntFlow(short tableId, Ipv6Prefix ipv6Prefix, BigInteger dpId, Long elanTag,
+            int addOrRemove) {
+        List<MatchInfo> naMatch = getIcmpv6NAMatch(elanTag);
+        naMatch.add(new MatchIpv6Source(ipv6Prefix));
+
         List<InstructionInfo> instructions = new ArrayList<>();
         List<ActionInfo> actionsInfos = new ArrayList<>();
-        // Punt to controller
         actionsInfos.add(new ActionPuntToController());
+        actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
         instructions.add(new InstructionApplyActions(actionsInfos));
+
+        String flowId = getIPv6FlowRef(dpId, elanTag, "IPv6NA." + ipv6Prefix.getValue());
         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
-                getIPv6FlowRef(dpId, elanTag, "IPv6RS"),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
-                NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
-        if (addOrRemove == Ipv6Constants.DEL_FLOW) {
-            LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
+                flowId, Ipv6ServiceConstants.PUNT_NA_FLOW_PRIORITY,
+                "IPv6NA", 0, 0, NwConstants.COOKIE_IPV6_TABLE, naMatch, instructions);
+        if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
+            LOG.trace("Removing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}", dpId, elanTag);
             mdsalUtil.removeFlow(rsFlowEntity);
         } else {
-            LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
+            LOG.trace("Installing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}", dpId, elanTag);
             mdsalUtil.installFlow(rsFlowEntity);
         }
     }
@@ -441,7 +367,7 @@ public class Ipv6ServiceUtils {
                                           BigInteger cookie, List<Instruction> instructions) {
         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
                 .setFlowPriority(flowPriority).setInstruction(instructions);
-        return new BoundServicesBuilder().setKey(new BoundServicesKey(servicePriority))
+        return new BoundServicesBuilder().withKey(new BoundServicesKey(servicePriority))
                 .setServiceName(serviceName).setServicePriority(servicePriority)
                 .setServiceType(ServiceTypeFlowBased.class)
                 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
@@ -464,7 +390,7 @@ public class Ipv6ServiceUtils {
         BoundServices
                 serviceInfo =
                 getBoundServices(String.format("%s.%s", "ipv6", interfaceName),
-                        serviceIndex, Ipv6Constants.DEFAULT_FLOW_PRIORITY,
+                        serviceIndex, Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY,
                         NwConstants.COOKIE_IPV6_TABLE, instructions);
         MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
                 buildServiceId(interfaceName, serviceIndex), serviceInfo);
@@ -475,4 +401,34 @@ public class Ipv6ServiceUtils {
                 buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME,
                         NwConstants.IPV6_SERVICE_INDEX)));
     }
+
+    @Nullable
+    public BigInteger getDpIdFromInterfaceState(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
+            .interfaces.rev140508.interfaces.state.Interface interfaceState) {
+        BigInteger dpId = null;
+        List<String> ofportIds = interfaceState.getLowerLayerIf();
+        if (ofportIds != null && !ofportIds.isEmpty()) {
+            NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
+            dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
+        }
+        return dpId;
+    }
+
+    public static long getRemoteBCGroup(long elanTag) {
+        return Ipv6ServiceConstants.ELAN_GID_MIN + elanTag % Ipv6ServiceConstants.ELAN_GID_MIN * 2;
+    }
+
+    public static boolean isVmPort(String deviceOwner) {
+        // FIXME: Currently for VM ports, Neutron is sending deviceOwner as empty instead of "compute:nova".
+        // return Ipv6ServiceConstants.DEVICE_OWNER_COMPUTE_NOVA.equalsIgnoreCase(deviceOwner);
+        return Ipv6ServiceConstants.DEVICE_OWNER_COMPUTE_NOVA.equalsIgnoreCase(deviceOwner)
+                || StringUtils.isEmpty(deviceOwner);
+    }
+
+    public static boolean isIpv6Subnet(VirtualSubnet subnet) {
+        if (subnet == null) {
+            return false;
+        }
+        return subnet.getIpVersion().equals(Ipv6ServiceConstants.IP_VERSION_V6) ? true : false;
+    }
 }