NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / SubnetRoutePacketInHandler.java
index 972d3f723d5fc37fa2216e03fee612a24bf6a328..f30a102b5a1da4911f8c679af379159e46593049 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016, 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright (c) 2016, 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -7,18 +7,15 @@
  */
 package org.opendaylight.netvirt.vpnmanager;
 
-import com.google.common.base.Optional;
 import com.google.common.primitives.Ints;
-import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import javax.inject.Inject;
 import javax.inject.Singleton;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
@@ -26,29 +23,46 @@ import org.opendaylight.genius.mdsalutil.NWUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
 import org.opendaylight.genius.mdsalutil.packet.IPv4;
+import org.opendaylight.infrautils.metrics.Counter;
+import org.opendaylight.infrautils.metrics.Labeled;
+import org.opendaylight.infrautils.metrics.MetricDescriptor;
+import org.opendaylight.infrautils.metrics.MetricProvider;
 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.netvirt.vpnmanager.api.ICentralizedSwitchProvider;
 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
-import org.opendaylight.netvirt.vpnmanager.utilities.VpnManagerCounters;
+import org.opendaylight.netvirt.vpnmanager.iplearn.ipv4.ArpUtils;
+import org.opendaylight.netvirt.vpnmanager.utilities.CounterUtility;
+import org.opendaylight.openflowplugin.libraries.liblldp.BitBufferHelper;
+import org.opendaylight.openflowplugin.libraries.liblldp.BufferException;
 import org.opendaylight.openflowplugin.libraries.liblldp.HexEncode;
-import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
 import org.opendaylight.openflowplugin.libraries.liblldp.Packet;
 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
+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.IpPrefixBuilder;
+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.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.genius.interfacemanager.rev160406.IfTunnel;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.tag.name.map.ElanTagName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.interfaces.VpnInterface;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -61,113 +75,102 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
     private final OdlInterfaceRpcService odlInterfaceRpcService;
     private final ICentralizedSwitchProvider centralizedSwitchProvider;
     private final IInterfaceManager interfaceManager;
-    private final VpnManagerCounters vpnManagerCounters;
+    private final Ipv6NdUtilService ipv6NdUtilService;
+    private final Labeled<Labeled<Counter>> packetInCounter;
+    private final VpnUtil vpnUtil;
 
     @Inject
     public SubnetRoutePacketInHandler(final DataBroker dataBroker, final PacketProcessingService packetService,
             final OdlInterfaceRpcService odlInterfaceRpcService,
-            final ICentralizedSwitchProvider centralizedSwitchProvider,
-            final IInterfaceManager interfaceManager, VpnManagerCounters vpnManagerCounters) {
+            final ICentralizedSwitchProvider centralizedSwitchProvider, final IInterfaceManager interfaceManager,
+            final Ipv6NdUtilService ipv6NdUtilService, MetricProvider metricProvider, VpnUtil vpnUtil) {
         this.dataBroker = dataBroker;
         this.packetService = packetService;
         this.odlInterfaceRpcService = odlInterfaceRpcService;
         this.centralizedSwitchProvider = centralizedSwitchProvider;
         this.interfaceManager = interfaceManager;
-        this.vpnManagerCounters = vpnManagerCounters;
+        this.ipv6NdUtilService = ipv6NdUtilService;
+        packetInCounter =  metricProvider.newCounter(MetricDescriptor.builder().anchor(this)
+                .project(CounterUtility.getProject()).module(CounterUtility.getModule())
+                .id(CounterUtility.getSubnetRouteId()).build(), "action","sourceIp.destinationIp");
+        this.vpnUtil = vpnUtil;
     }
 
     @Override
     public void onPacketReceived(PacketReceived notification) {
 
-        short tableId = notification.getTableId().getValue();
+        short tableId = notification.getTableId().getValue().toJava();
         LOG.trace("{} onPacketReceived: Packet punted from table {}", LOGGING_PREFIX, tableId);
+        if (!vpnUtil.isArpLearningEnabled()) {
+            LOG.trace("Not handling packet as ARP Based Learning is disabled");
+            return;
+        }
         byte[] data = notification.getPayload();
         if (notification.getMatch() == null || notification.getMatch().getMetadata() == null) {
             LOG.error("{} onPacketReceived: Received from table {} where the match or metadata are null",
                     LOGGING_PREFIX, tableId);
             return;
         }
-        BigInteger metadata = notification.getMatch().getMetadata().getMetadata();
+        Uint64 metadata = notification.getMatch().getMetadata().getMetadata();
         Ethernet res = new Ethernet();
 
         if (tableId == NwConstants.L3_SUBNET_ROUTE_TABLE) {
             LOG.trace("{} onPacketReceived: Some packet received as {}", LOGGING_PREFIX, notification);
             try {
-                res.deserialize(data, 0, data.length * NetUtils.NUM_BITS_IN_A_BYTE);
+                res.deserialize(data, 0, data.length * Byte.SIZE);
             } catch (PacketException e) {
                 LOG.error("{} onPacketReceived: Failed to decode Packet ", LOGGING_PREFIX, e);
-                vpnManagerCounters.subnetRoutePacketFailed();
+                Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                        .label(CounterUtility.getSubnetRouteInvalidPacket());
+                counter.increment();
                 return;
             }
+            byte[] srcIpBytes = null;
+            byte[] dstIpBytes = null;
+            String srcIpStr;
+            String dstIpStr;
+            String srcMac = NWUtil.toStringMacAddress(res.getSourceMACAddress());
             try {
                 Packet pkt = res.getPayload();
                 if (pkt instanceof IPv4) {
                     IPv4 ipv4 = (IPv4) pkt;
-                    byte[] srcIp = Ints.toByteArray(ipv4.getSourceAddress());
-                    byte[] dstIp = Ints.toByteArray(ipv4.getDestinationAddress());
-                    String dstIpStr = NWUtil.toStringIpAddress(dstIp);
-                    String srcIpStr = NWUtil.toStringIpAddress(srcIp);
+                    srcIpBytes = Ints.toByteArray(ipv4.getSourceAddress());
+                    dstIpBytes = Ints.toByteArray(ipv4.getDestinationAddress());
+                    srcIpStr = NWUtil.toStringIpAddress(srcIpBytes);
+                    dstIpStr = NWUtil.toStringIpAddress(dstIpBytes);
+                    Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_recived.toString())
+                            .label(srcIpStr + "." + dstIpStr);
+                    counter.increment();
                     // It is an ARP request on a configured VPN. So we must
                     // attempt to respond.
-                    long vpnId = MetaDataUtil.getVpnIdFromMetadata(metadata);
-
-                    LOG.info("{} onPacketReceived: Processing IPv4 Packet received with Source IP {} and Target IP {}"
-                            + " and vpnId {}", LOGGING_PREFIX, srcIpStr, dstIpStr, vpnId);
-
-                    Optional<VpnIds> vpnIdsOptional = SingleTransactionDataBroker.syncReadOptional(dataBroker,
-                            LogicalDatastoreType.CONFIGURATION, VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId));
-
-                    if (!vpnIdsOptional.isPresent()) {
-                        // Donot trigger subnetroute logic for packets from
-                        // unknown VPNs
-                        vpnManagerCounters.subnetRoutePacketIgnored();
-                        LOG.info("{} onPacketReceived: Ignoring IPv4 packet with destination Ip {} and source Ip {}"
-                                + " as it came on unknown VPN with ID {}", LOGGING_PREFIX, dstIpStr, srcIpStr, vpnId);
-                        return;
-                    }
-
-                    String vpnIdVpnInstanceName = vpnIdsOptional.get().getVpnInstanceName();
-                    if (VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnIdVpnInstanceName, dstIpStr) != null) {
-                        vpnManagerCounters.subnetRoutePacketIgnored();
-                        LOG.info("{} onPacketReceived: IPv4 Packet received with Target IP {} source IP {} vpnId {} "
-                                + "is a valid Neutron port,ignoring subnet route processing", LOGGING_PREFIX, dstIpStr,
-                                srcIp, vpnId);
-                        return;
-                    }
-
-                    if (VpnUtil.getLearntVpnVipToPort(dataBroker, vpnIdVpnInstanceName, dstIpStr) != null) {
-                        vpnManagerCounters.subnetRoutePacketIgnored();
-                        LOG.info("{} onPacketReceived: IPv4 Packet received with Target IP {} source Ip {} vpnId {}"
-                                + " is an already discovered IPAddress, ignoring subnet route processing",
-                                LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
-                        return;
-                    }
-
-                    long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
-                    if (elanTag == 0L) {
-                        vpnManagerCounters.subnetRoutePacketFailed();
-                        LOG.error("{} onPacketReceived: elanTag value from metadata found to be 0, for IPv4 "
-                                + " Packet received with Target IP {} src Ip {} vpnId {}",
-                                LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
-                        return;
-                    }
-
-                    if (!vpnIdsOptional.get().isExternalVpn()) {
-                        handleInternalVpnSubnetRoutePacket(metadata, dstIp, srcIpStr, dstIpStr,
-                                ipv4.getDestinationAddress(), vpnIdVpnInstanceName, elanTag);
-                        return;
+                } else {
+                    // IPv6 case
+                    // TODO: IPv6 deserializer
+                    int ethType = BitBufferHelper.getInt(
+                            BitBufferHelper.getBits(data, VpnConstants.ETHTYPE_START, VpnConstants.TWO_BYTES));
+                    if (ethType == VpnConstants.IP_V6_ETHTYPE) {
+                        srcIpBytes = BitBufferHelper.getBits(data, VpnConstants.IP_V6_HDR_START + 64, 128);
+                        dstIpBytes = BitBufferHelper.getBits(data, VpnConstants.IP_V6_HDR_START + 192, 128);
                     }
-
-                    byte[] srcMac = res.getSourceMACAddress();
-                    handleBgpVpnSubnetRoute(ipv4, srcMac, dstIp, dstIpStr, srcIpStr, elanTag);
                 }
-            } catch (UnknownHostException | InterruptedException | ExecutionException ex) {
+                if (srcIpBytes == null || dstIpBytes == null) {
+                    LOG.trace("{} onPacketReceived: Non-IP packet received as {}", LOGGING_PREFIX, notification);
+                    return;
+                }
+                handleIpPackets(srcIpBytes, dstIpBytes, NWUtil.toStringIpAddress(srcIpBytes),
+                        NWUtil.toStringIpAddress(dstIpBytes), srcMac, metadata);
+
+            } catch (InterruptedException | ExecutionException | BufferException ex) {
                 // Failed to handle packet
-                vpnManagerCounters.subnetRoutePacketFailed();
+                Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                        .label(NWUtil.toStringIpAddress(srcIpBytes) + "." + NWUtil.toStringIpAddress(srcIpBytes));
+                counter.increment();
                 LOG.error("{} onPacketReceived: Failed to handle subnetroute packet.", LOGGING_PREFIX, ex);
-            } catch (ReadFailedException e) {
-                vpnManagerCounters.subnetRoutePacketFailed();
-                LOG.error("{} onPacketReceived: Failed to read data-store.", LOGGING_PREFIX, e);
+            } catch (UnknownHostException e) {
+                Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                        .label(CounterUtility.getSubnetRouteInvalidPacket());
+                counter.increment();
+                LOG.error("{} onPacketReceived: Unknown host detected while handling subnetroute", LOGGING_PREFIX, e);
             }
             return;
         }
@@ -176,81 +179,162 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
 
     }
 
-    private void handleBgpVpnSubnetRoute(IPv4 ipv4, byte[] srcMac, byte[] dstIp, String dstIpStr, String srcIpStr,
+    private void handleIpPackets(byte[] srcIp, byte[] dstIp, String srcIpStr, String dstIpStr, String srcMac,
+            Uint64 metadata)
+            throws UnknownHostException, InterruptedException, ExecutionException {
+        Uint32 vpnId = Uint32.valueOf(MetaDataUtil.getVpnIdFromMetadata(metadata));
+
+        LOG.info("{} onPacketReceived: Processing IP Packet received with Source IP {} and Target IP {}"
+                + " and vpnId {}", LOGGING_PREFIX, srcIpStr, dstIpStr, vpnId);
+
+        Optional<VpnIds> vpnIdsOptional = SingleTransactionDataBroker.syncReadOptional(dataBroker,
+                LogicalDatastoreType.CONFIGURATION, VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId));
+
+        if (!vpnIdsOptional.isPresent()) {
+            // Donot trigger subnetroute logic for packets from
+            // unknown VPNs
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_drop.toString())
+                    .label(srcIpStr + "." + dstIpStr);
+            counter.increment();
+            LOG.info("{} onPacketReceived: Ignoring IPv4 packet with destination Ip {} and source Ip {}"
+                    + " as it came on unknown VPN with ID {}", LOGGING_PREFIX, dstIpStr, srcIpStr, vpnId);
+            return;
+        }
+
+        String vpnIdVpnInstanceName = vpnIdsOptional.get().getVpnInstanceName();
+        VpnPortipToPort persistedIP =
+                vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnIdVpnInstanceName, dstIpStr);
+        if (persistedIP != null && !persistedIP.isLearntIp()) {
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_drop.toString())
+                    .label(srcIpStr + "." + dstIpStr);
+            counter.increment();
+            LOG.info("{} onPacketReceived: IP Packet received with Target IP {} source IP {} vpnId {} "
+                    + "is a valid Neutron port,ignoring subnet route processing", LOGGING_PREFIX, dstIpStr,
+                    srcIp, vpnId);
+            return;
+        }
+
+        if (vpnUtil.getLearntVpnVipToPort(vpnIdVpnInstanceName, dstIpStr) != null) {
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label(srcIpStr + "." + dstIpStr);
+            counter.increment();
+            LOG.info("{} onPacketReceived: IP Packet received with Target IP {} source Ip {} vpnId {}"
+                    + " is an already discovered IPAddress, ignoring subnet route processing",
+                    LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
+            return;
+        }
+
+        long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
+        if (elanTag == 0L) {
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label(srcIpStr + "." + dstIpStr);
+            counter.increment();
+            LOG.error("{} onPacketReceived: elanTag value from metadata found to be 0, for IP "
+                    + " Packet received with Target IP {} src Ip {} vpnId {}",
+                    LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
+            return;
+        }
+
+        if (!vpnIdsOptional.get().isExternalVpn()) {
+            handleInternalVpnSubnetRoutePacket(metadata, dstIp, srcIpStr, dstIpStr, vpnIdVpnInstanceName,
+                    elanTag);
+            return;
+        }
+
+        handleBgpVpnSubnetRoute(srcMac, dstIp, dstIpStr, srcIpStr, elanTag);
+    }
+
+    private void handleBgpVpnSubnetRoute(String srcMac, byte[] dstIp, String dstIpStr, String srcIpStr,
             long elanTag) throws UnknownHostException {
-        LOG.info("{} handleBgpVpnSubnetRoute: Processing IPv4 Packet received with Source IP {} and Target IP {}"
+        LOG.info("{} handleBgpVpnSubnetRoute: Processing IP Packet received with Source IP {} and Target IP {}"
                 + " and elan Tag {}", LOGGING_PREFIX, srcIpStr, dstIpStr, elanTag);
         SubnetOpDataEntry targetSubnetForPacketOut =
-                getTargetSubnetForPacketOut(dataBroker, elanTag, ipv4.getDestinationAddress());
-
+                getTargetSubnetForPacketOut(elanTag, dstIpStr);
         if (targetSubnetForPacketOut != null) {
             // Handle subnet routes ip requests
-            transmitArpPacket(targetSubnetForPacketOut.getNhDpnId(), srcIpStr, NWUtil.toStringMacAddress(srcMac), dstIp,
-                    elanTag);
+            transmitArpOrNsPacket(targetSubnetForPacketOut.getNhDpnId(),
+                                        srcIpStr, srcMac, dstIp, dstIpStr, elanTag);
         } else {
-            vpnManagerCounters.subnetRoutePacketFailed();
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label(srcIpStr + "." + dstIpStr);
+            counter.increment();
             LOG.debug("{} handleBgpVpnSubnetRoute: Could not find target subnet for packet out {}", LOGGING_PREFIX,
                     dstIpStr);
         }
     }
 
-    private void handleInternalVpnSubnetRoutePacket(BigInteger metadata, byte[] dstIp, String srcIpStr, String dstIpStr,
-            int destinationAddress, String vpnIdVpnInstanceName, long elanTag)
+    private void handleInternalVpnSubnetRoutePacket(Uint64 metadata, byte[] dstIp, String srcIpStr, String dstIpStr,
+            String vpnIdVpnInstanceName, long elanTag)
             throws InterruptedException, ExecutionException, UnknownHostException {
-        String vmVpnInterfaceName = VpnUtil.getVpnInterfaceName(odlInterfaceRpcService, metadata);
+        String vmVpnInterfaceName = vpnUtil.getVpnInterfaceName(metadata);
         if (isTunnel(vmVpnInterfaceName)) {
-            handlePacketFromTunnelToExternalNetwork(vpnIdVpnInstanceName,
-                    srcIpStr, dstIp, elanTag);
+            handlePacketFromTunnelToExternalNetwork(vpnIdVpnInstanceName, srcIpStr, dstIp, dstIpStr, elanTag);
         }
-        VpnInterface vmVpnInterface = VpnUtil.getVpnInterface(dataBroker, vmVpnInterfaceName);
+        VpnInterface vmVpnInterface = vpnUtil.getVpnInterface(vmVpnInterfaceName);
         if (vmVpnInterface == null) {
             LOG.error("Vpn interface {} doesn't exist.", vmVpnInterfaceName);
-            vpnManagerCounters.subnetRoutePacketFailed();
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label(srcIpStr + "." + dstIpStr);
+            counter.increment();
             return;
         }
         if (VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnIdVpnInstanceName,
                vmVpnInterface.getVpnInstanceNames())
-               && !VpnUtil.isBgpVpnInternet(dataBroker, vpnIdVpnInstanceName)) {
+               && !vpnUtil.isBgpVpnInternet(vpnIdVpnInstanceName)) {
             LOG.trace("Unknown IP is in internal network");
-            handlePacketToInternalNetwork(dstIp, dstIpStr, destinationAddress, elanTag);
+            handlePacketToInternalNetwork(dstIp, dstIpStr, elanTag, srcIpStr);
         } else {
             LOG.trace("Unknown IP is in external network");
-            String vpnName = VpnUtil.getInternetVpnFromVpnInstanceList(dataBroker,
-                                vmVpnInterface.getVpnInstanceNames());
+            String vpnName = vpnUtil.getInternetVpnFromVpnInstanceList(vmVpnInterface.getVpnInstanceNames());
             if (vpnName != null) {
-                handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName),
-                                   vpnName, dstIp, elanTag);
+                handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), vpnName, dstIp, dstIpStr, elanTag);
             } else {
                 vpnName = VpnHelper.getFirstVpnNameFromVpnInterface(vmVpnInterface);
-                LOG.trace("Unknown IP is in external network, but internet VPN not found."
-                         + " fallback to first VPN");
-                handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName),
-                                   vpnName, dstIp, elanTag);
+                LOG.trace("Unknown IP is in external network, but internet VPN not found." + " fallback to first VPN");
+                handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), vpnName, dstIp, dstIpStr, elanTag);
 
             }
         }
     }
 
-    private void transmitArpPacket(BigInteger dpnId, String sourceIpAddress, String sourceMac, byte[] dstIp,
-            long elanTag) throws UnknownHostException {
-        LOG.debug("Sending arp with elan tag {}", elanTag);
-        vpnManagerCounters.subnetRoutePacketArpSent();
+    private void transmitArpOrNsPacket(Uint64 dpnId, String sourceIpAddress, String sourceMac, byte[] dstIpBytes,
+            String dstIpAddress, long elanTag) throws UnknownHostException {
         long groupid = VpnUtil.getRemoteBCGroup(elanTag);
-        TransmitPacketInput arpRequestInput = ArpUtils.createArpRequestInput(dpnId, groupid,
-                HexEncode.bytesFromHexString(sourceMac), InetAddress.getByName(sourceIpAddress).getAddress(), dstIp);
-
-        JdkFutures.addErrorLogging(packetService.transmitPacket(arpRequestInput), LOG, "Transmit packet");
+        if (NWUtil.isIpv4Address(dstIpAddress)) {
+            LOG.debug("Sending ARP: srcIp={}, srcMac={}, dstIp={}, dpId={}, elan-tag={}, groupid={}", sourceIpAddress,
+                    sourceMac, dstIpAddress, dpnId, elanTag, groupid);
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_arp_sent.toString())
+                    .label(sourceIpAddress + "." + dstIpAddress);
+            counter.increment();
+
+            TransmitPacketInput packetInput =
+                    ArpUtils.createArpRequestInput(dpnId, groupid, HexEncode.bytesFromHexString(sourceMac),
+                            InetAddress.getByName(sourceIpAddress).getAddress(), dstIpBytes);
+            JdkFutures.addErrorLogging(packetService.transmitPacket(packetInput), LOG, "Transmit packet");
+        } else {
+            // IPv6 case
+            LOG.debug("Sending NS: srcIp={}, srcMac={}, dstIp={}, dpId={}, elan-tag={}, groupid={}", sourceIpAddress,
+                    sourceMac, dstIpAddress, dpnId, elanTag, groupid);
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_ns_sent.toString())
+                    .label(sourceIpAddress + "." + dstIpAddress);
+            counter.increment();
+
+            VpnUtil.sendNeighborSolicationToOfGroup(this.ipv6NdUtilService, new Ipv6Address(sourceIpAddress),
+                    new MacAddress(sourceMac), new Ipv6Address(dstIpAddress), groupid, dpnId);
+        }
     }
 
-    private void handlePacketToInternalNetwork(byte[] dstIp, String dstIpStr, int destinationAddress, long elanTag)
+    private void handlePacketToInternalNetwork(byte[] dstIp, String dstIpStr, long elanTag, String srcIpStr)
             throws UnknownHostException {
         try {
             SubnetOpDataEntry targetSubnetForPacketOut =
-                    getTargetSubnetForPacketOut(dataBroker, elanTag, destinationAddress);
+                    getTargetSubnetForPacketOut(elanTag, dstIpStr);
 
             if (targetSubnetForPacketOut == null) {
                 LOG.debug("Couldn't find matching subnet for elan tag {} and destination ip {}", elanTag, dstIpStr);
-                vpnManagerCounters.subnetRoutePacketFailed();
+                Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                        .label(srcIpStr + "." + dstIpStr);
+                counter.increment();
                 return;
             }
 
@@ -259,14 +343,18 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
                     VpnUtil.buildSubnetmapIdentifier(targetSubnetForPacketOut.getSubnetId()));
             if (!subnetMap.isPresent()) {
                 LOG.debug("Couldn't find subnet map for subnet {}", targetSubnetForPacketOut.getSubnetId());
-                vpnManagerCounters.subnetRoutePacketFailed();
+                Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                        .label(srcIpStr + "." + dstIpStr);
+                counter.increment();
                 return;
             }
 
             String sourceIp = subnetMap.get().getRouterInterfaceFixedIp();
             if (sourceIp == null) {
                 LOG.debug("Subnet map {} doesn't have a router interface ip defined", subnetMap.get().getId());
-                vpnManagerCounters.subnetRoutePacketFailed();
+                Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                        .label("." + dstIpStr);
+                counter.increment();
                 return;
             }
 
@@ -274,32 +362,37 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
             if (sourceMac == null) {
                 LOG.debug("Subnet map {} doesn't have a router interface mac address defined",
                         subnetMap.get().getId());
-                vpnManagerCounters.subnetRoutePacketFailed();
+                Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                        .label(sourceIp + "." + dstIpStr);
+                counter.increment();
                 return;
             }
 
-            transmitArpPacket(targetSubnetForPacketOut.getNhDpnId(), sourceIp, sourceMac, dstIp, elanTag);
-        } catch (ReadFailedException e) {
+            transmitArpOrNsPacket(targetSubnetForPacketOut.getNhDpnId(),
+                                        sourceIp, sourceMac, dstIp, dstIpStr, elanTag);
+        } catch (InterruptedException | ExecutionException e) {
             LOG.error("handlePacketToInternalNetwork: Failed to read data store for destIp {} elanTag {}", dstIpStr,
                     elanTag);
         }
     }
 
-    private void handlePacketFromTunnelToExternalNetwork(String vpnIdVpnInstanceName,
-                        String srcIpStr, byte[] dstIp, long elanTag)
-                                throws UnknownHostException {
-        String routerId = VpnUtil.getAssociatedExternalRouter(dataBroker, srcIpStr);
+    private void handlePacketFromTunnelToExternalNetwork(String vpnIdVpnInstanceName, String srcIpStr, byte[] dstIp,
+        String dstIpStr, long elanTag) throws UnknownHostException {
+        String routerId = vpnUtil.getAssociatedExternalRouter(srcIpStr);
         if (null == routerId) {
             LOG.debug("This ip is not associated with any external router: {}", srcIpStr);
+            return;
         }
-        handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), routerId, dstIp, elanTag);
+        handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), routerId, dstIp, dstIpStr, elanTag);
     }
 
-    private void handlePacketToExternalNetwork(Uuid vpnInstanceNameUuid, String routerId, byte[] dstIp,
+    private void handlePacketToExternalNetwork(Uuid vpnInstanceNameUuid, String routerId, byte[] dstIp, String dstIpStr,
             long elanTag) throws UnknownHostException {
-        Routers externalRouter = VpnUtil.getExternalRouter(dataBroker, routerId);
+        Routers externalRouter = vpnUtil.getExternalRouter(routerId);
         if (externalRouter == null) {
-            vpnManagerCounters.subnetRoutePacketFailed();
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label("." + dstIpStr);
+            counter.increment();
             LOG.debug("{} handlePacketToExternalNetwork: Can't find external router with id {}", LOGGING_PREFIX,
                     routerId);
             return;
@@ -307,7 +400,9 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
 
         List<ExternalIps> externalIps = externalRouter.getExternalIps();
         if (externalIps == null || externalIps.isEmpty()) {
-            vpnManagerCounters.subnetRoutePacketFailed();
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label("." + dstIpStr);
+            counter.increment();
             LOG.debug("{} handlePacketToExternalNetwork: Router {} doesn't have any external ips.",
                     LOGGING_PREFIX, externalRouter.getRouterName());
             return;
@@ -316,33 +411,43 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
         java.util.Optional<ExternalIps> externalIp = externalRouter.getExternalIps().stream()
                 .filter(eip -> vpnInstanceNameUuid.equals(eip.getSubnetId())).findFirst();
         if (!externalIp.isPresent()) {
-            vpnManagerCounters.subnetRoutePacketFailed();
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label(externalIp.get().getIpAddress() + "." + dstIpStr);
+            counter.increment();
             LOG.debug("{} handlePacketToExternalNetwork: Router {} doesn't have an external ip for subnet id {}.",
                     LOGGING_PREFIX, externalRouter.getRouterName(), vpnInstanceNameUuid);
             return;
         }
 
-        BigInteger dpnId = centralizedSwitchProvider.getPrimarySwitchForRouter(externalRouter.getRouterName());
-        if (BigInteger.ZERO.equals(dpnId)) {
-            vpnManagerCounters.subnetRoutePacketFailed();
+        Uint64 dpnId = centralizedSwitchProvider.getPrimarySwitchForRouter(externalRouter.getRouterName());
+        if (Uint64.ZERO.equals(dpnId)) {
+            Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
+                    .label(externalIp.get().getIpAddress() + "." + dstIpStr);
+            counter.increment();
             LOG.debug("{} handlePacketToExternalNetwork: Could not find primary switch for router {}.",
                     LOGGING_PREFIX, externalRouter.getRouterName());
             return;
         }
 
-        transmitArpPacket(dpnId, externalIp.get().getIpAddress(), externalRouter.getExtGwMacAddress(), dstIp, elanTag);
+        transmitArpOrNsPacket(dpnId, externalIp.get().getIpAddress(), externalRouter.getExtGwMacAddress(), dstIp,
+                dstIpStr, elanTag);
+
+        Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_processed.toString())
+                .label(externalIp.get().getIpAddress() + "." + dstIpStr);
+        counter.increment();
     }
 
     // return only the first VPN subnetopdataentry
-    private static SubnetOpDataEntry getTargetSubnetForPacketOut(DataBroker broker, long elanTag, int ipAddress) {
-        ElanTagName elanInfo = VpnUtil.getElanInfoByElanTag(broker, elanTag);
+    @Nullable
+    private SubnetOpDataEntry getTargetSubnetForPacketOut(long elanTag, String ipAddress) {
+        ElanTagName elanInfo = vpnUtil.getElanInfoByElanTag(elanTag);
         if (elanInfo == null) {
             LOG.error("{} getTargetDpnForPacketOut: Unable to retrieve ElanInfo for elanTag {}", LOGGING_PREFIX,
                     elanTag);
             return null;
         }
         try {
-            Optional<NetworkMap> optionalNetworkMap = SingleTransactionDataBroker.syncReadOptional(broker,
+            Optional<NetworkMap> optionalNetworkMap = SingleTransactionDataBroker.syncReadOptional(dataBroker,
                     LogicalDatastoreType.CONFIGURATION, VpnUtil.buildNetworkMapIdentifier(new Uuid(
                             elanInfo.getName())));
             if (!optionalNetworkMap.isPresent()) {
@@ -353,33 +458,36 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
             List<Uuid> subnetList = optionalNetworkMap.get().getSubnetIdList();
             LOG.debug("{} getTargetDpnForPacketOut: Obtained subnetList as {} for network {}", LOGGING_PREFIX,
                     subnetList, elanInfo.getName());
-            for (Uuid subnetId : subnetList) {
-                String vpnName = null;
-                Subnetmap sn = VpnUtil.getSubnetmapFromItsUuid(broker, subnetId);
-                if (sn != null && sn.getVpnId() != null) {
-                    vpnName = sn.getVpnId().getValue();
-                }
-                if (vpnName == null) {
-                    continue;
-                }
-                Optional<SubnetOpDataEntry> optionalSubs;
-                optionalSubs = SingleTransactionDataBroker.syncReadOptional(broker, LogicalDatastoreType.OPERATIONAL,
-                        VpnUtil.buildSubnetOpDataEntryInstanceIdentifier(subnetId));
-                if (!optionalSubs.isPresent()) {
-                    continue;
-                }
-                SubnetOpDataEntry subOpEntry = optionalSubs.get();
-                if (subOpEntry.getNhDpnId() != null) {
-                    LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {}", LOGGING_PREFIX, subnetId.getValue());
-                    boolean match = NWUtil.isIpInSubnet(ipAddress, subOpEntry.getSubnetCidr());
-                    LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {} matching {}", LOGGING_PREFIX,
+            if (subnetList != null) {
+                for (Uuid subnetId : subnetList) {
+                    String vpnName = null;
+                    Subnetmap sn = vpnUtil.getSubnetmapFromItsUuid(subnetId);
+                    if (sn != null && sn.getVpnId() != null) {
+                        vpnName = sn.getVpnId().getValue();
+                    }
+                    if (vpnName == null) {
+                        continue;
+                    }
+                    Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
+                        LogicalDatastoreType.OPERATIONAL, VpnUtil.buildSubnetOpDataEntryInstanceIdentifier(subnetId));
+                    if (!optionalSubs.isPresent()) {
+                        continue;
+                    }
+                    SubnetOpDataEntry subOpEntry = optionalSubs.get();
+                    if (subOpEntry.getNhDpnId() != null) {
+                        LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {}", LOGGING_PREFIX,
+                            subnetId.getValue());
+                        IpPrefix cidr = IpPrefixBuilder.getDefaultInstance(subOpEntry.getSubnetCidr());
+                        boolean match = NWUtil.isIpAddressInRange(IpAddressBuilder.getDefaultInstance(ipAddress), cidr);
+                        LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {} matching {}", LOGGING_PREFIX,
                             subnetId.getValue(), match);
-                    if (match) {
-                        return subOpEntry;
+                        if (match) {
+                            return subOpEntry;
+                        }
                     }
                 }
             }
-        } catch (ReadFailedException e) {
+        } catch (InterruptedException | ExecutionException e) {
             LOG.error("{} getTargetDpnForPacketOut: Failed to read data store for elan {}", LOGGING_PREFIX,
                     elanInfo.getName());
         }
@@ -387,9 +495,6 @@ public class SubnetRoutePacketInHandler implements PacketProcessingListener {
     }
 
     public boolean isTunnel(String interfaceName) {
-        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
-            .ietf.interfaces.rev140508.interfaces.Interface configIface =
-            interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
-        return configIface.augmentation(IfTunnel.class) != null;
+        return interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName).augmentation(IfTunnel.class) != null;
     }
 }