Bug9016:Using Single Transaction during NAPT SwitchOver
[netvirt.git] / vpnservice / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NaptEventHandler.java
index e9532c59991537dff2230990fad0e9781eea032d..5114622678e858358911d84a3b7d35dcc35c68ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright © 2016, 2017 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,
 
 package org.opendaylight.netvirt.natservice.internal;
 
-import org.opendaylight.genius.mdsalutil.*;
-import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
-import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
 import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
+import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
+import org.opendaylight.genius.mdsalutil.ActionInfo;
+import org.opendaylight.genius.mdsalutil.FlowEntity;
+import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
+import org.opendaylight.genius.mdsalutil.InstructionInfo;
+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.actions.ActionOutput;
+import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetDestinationIp;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpDestinationPort;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpSourcePort;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpDestinationPort;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpSourcePort;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
+import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
+import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
+import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
+import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
+import org.opendaylight.genius.mdsalutil.matches.MatchTcpDestinationPort;
+import org.opendaylight.genius.mdsalutil.matches.MatchTcpSourcePort;
+import org.opendaylight.genius.mdsalutil.matches.MatchUdpDestinationPort;
+import org.opendaylight.genius.mdsalutil.matches.MatchUdpSourcePort;
+import org.opendaylight.genius.mdsalutil.packet.Ethernet;
+import org.opendaylight.genius.mdsalutil.packet.IPv4;
+import org.opendaylight.genius.mdsalutil.packet.TCP;
+import org.opendaylight.genius.mdsalutil.packet.UDP;
+import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.netvirt.natservice.internal.NaptPacketInHandler.NatPacketProcessingState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+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.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
+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.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class NaptEventHandler {
-    private NaptManager naptManager;
     private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
+    private final DataBroker dataBroker;
     private static IMdsalApiManager mdsalManager;
-    private DataBroker dataBroker;
-
-    public NaptEventHandler(final DataBroker dataBroker) {
+    private final PacketProcessingService pktService;
+    private final OdlInterfaceRpcService interfaceManagerRpc;
+    private final NaptManager naptManager;
+    private final IElanService elanManager;
+    private final IdManagerService idManager;
+    private final IInterfaceManager interfaceManager;
+    private static SalFlowService salFlowServiceRpc;
+
+    @Inject
+    public NaptEventHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
+                            final NaptManager naptManager,
+                            final PacketProcessingService pktService,
+                            final OdlInterfaceRpcService interfaceManagerRpc,
+                            final IInterfaceManager interfaceManager,
+                            final IElanService elanManager,
+                            final IdManagerService idManager,
+                            final SalFlowService salFlowServiceRpc) {
         this.dataBroker = dataBroker;
-    }
-
-    public void setMdsalManager(IMdsalApiManager mdsalManager) {
-        this.mdsalManager = mdsalManager;
-    }
-
-    public void setNaptManager(NaptManager naptManager) {
+        NaptEventHandler.mdsalManager = mdsalManager;
         this.naptManager = naptManager;
+        this.pktService = pktService;
+        this.interfaceManagerRpc = interfaceManagerRpc;
+        this.interfaceManager = interfaceManager;
+        this.elanManager = elanManager;
+        this.idManager = idManager;
+        this.salFlowServiceRpc = salFlowServiceRpc;
     }
 
-
-    public void handleEvent(NAPTEntryEvent naptEntryEvent){
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void handleEvent(NAPTEntryEvent naptEntryEvent) {
     /*
             Flow programming logic of the OUTBOUND NAPT TABLE :
             1) Get the internal IP address, port number, router ID from the event.
@@ -60,104 +155,324 @@ public class NaptEventHandler {
             3) Write the router ID to the metadata.
             5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
     */
-        Long routerId = naptEntryEvent.getRouterId();
-        LOG.info("NAT Service : handleEvent() entry for IP {}, port {}, routerID {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
-
-        //Get the DPN ID
-        BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
-        long bgpVpnId = NatConstants.INVALID_ID;
-        if(dpnId == null ){
-            LOG.warn("NAT Service : dpnId is null. Assuming the router ID {} as the BGP VPN ID and proceeding....", routerId);
-            bgpVpnId = routerId;
-            LOG.debug("NAT Service : BGP VPN ID {}", bgpVpnId);
-            String vpnName = NatUtil.getRouterName(dataBroker, bgpVpnId);
-            String routerName = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName);
-            routerId = NatUtil.getVpnId(dataBroker, routerName);
-            LOG.debug("NAT Service : Router ID {}", routerId);
-            dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
-            if(dpnId == null){
-                LOG.error("NAT Service : dpnId is null for the router {}", routerId);
-                return;
+        try {
+            Long routerId = naptEntryEvent.getRouterId();
+            LOG.trace("handleEvent : Time Elapsed before procesing snat ({}:{}) packet is {} ms,"
+                    + "routerId: {},isPktProcessed:{}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(),
+                    (System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime()),
+                    routerId, naptEntryEvent.isPktProcessed());
+            //Get the DPN ID
+            BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
+            long bgpVpnId = NatConstants.INVALID_ID;
+            if (dpnId == null) {
+                LOG.warn("handleEvent : dpnId is null. Assuming the router ID {} as the BGP VPN ID and proceeding....",
+                    routerId);
+                bgpVpnId = routerId;
+                LOG.debug("handleEvent : BGP VPN ID {}", bgpVpnId);
+                String vpnName = NatUtil.getRouterName(dataBroker, bgpVpnId);
+                String routerName = NatUtil.getRouterIdfromVpnInstance(dataBroker, vpnName);
+                if (routerName == null) {
+                    LOG.error("handleEvent : Unable to find router for VpnName {}", vpnName);
+                    return;
+                }
+                routerId = NatUtil.getVpnId(dataBroker, routerName);
+                LOG.debug("handleEvent : Router ID {}", routerId);
+                dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
+                if (dpnId == null) {
+                    LOG.error("handleEvent : dpnId is null for the router {}", routerId);
+                    return;
+                }
             }
+            if (naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
+                LOG.debug("handleEvent : Inside Add operation of NaptEventHandler");
+
+                // Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
+                if (!naptEntryEvent.isPktProcessed()) {
+
+                    // Get the External Gateway MAC Address
+                    String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterId(dataBroker, routerId);
+                    if (extGwMacAddress != null) {
+                        LOG.debug("handleEvent : External Gateway MAC address {} found for External Router ID {}",
+                                extGwMacAddress, routerId);
+                    } else {
+                        LOG.error("handleEvent : No External Gateway MAC address found for External Router ID {}",
+                                routerId);
+                        return;
+                    }
+                    //Get the external network ID from the ExternalRouter model
+                    Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
+                    if (networkId == null) {
+                        LOG.error("handleEvent : networkId is null");
+                        return;
+                    }
+
+                    //Get the VPN ID from the ExternalNetworks model
+                    Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
+                    if (vpnUuid == null) {
+                        LOG.error("handleEvent : vpnUuid is null");
+                        return;
+                    }
+                    Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
+
+                    //Get the internal IpAddress, internal port number from the event
+                    String internalIpAddress = naptEntryEvent.getIpAddress();
+                    int internalPort = naptEntryEvent.getPortNumber();
+                    SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
+                    NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
+
+                    //Get the external IP address for the corresponding internal IP address
+                    SessionAddress externalAddress =
+                            naptManager.getExternalAddressMapping(routerId, internalAddress,
+                                    naptEntryEvent.getProtocol());
+                    if (externalAddress == null) {
+                        LOG.error("handleEvent : externalAddress is null");
+                        return;
+                    }
+
+                    Long vpnIdFromExternalSubnet = getVpnIdFromExternalSubnet(dataBroker, routerId,
+                            externalAddress.getIpAddress());
+                    if (vpnIdFromExternalSubnet != NatConstants.INVALID_ID) {
+                        vpnId = vpnIdFromExternalSubnet;
+                    }
+
+                    // Added External Gateway MAC Address
+                    Future<RpcResult<AddFlowOutput>> addFlowResult =
+                            buildAndInstallNatFlowsOptionalRpc(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnId, routerId,
+                                    bgpVpnId, externalAddress, internalAddress, protocol, extGwMacAddress, true);
+                    final BigInteger finalDpnId = dpnId;
+                    final Long finalVpnId = vpnId;
+                    final Long finalRouterId = routerId;
+                    final long finalBgpVpnId = bgpVpnId;
+                    Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
+                            new FutureCallback<RpcResult<AddFlowOutput>>() {
+
+                                @Override
+                                public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
+                                    LOG.debug("handleEvent : Configured inbound rule for {} to {}",
+                                             internalAddress, externalAddress);
+                                    Future<RpcResult<AddFlowOutput>> addFlowResult =
+                                            buildAndInstallNatFlowsOptionalRpc(finalDpnId,
+                                                    NwConstants.OUTBOUND_NAPT_TABLE, finalVpnId, finalRouterId,
+                                                    finalBgpVpnId, internalAddress, externalAddress, protocol,
+                                                    extGwMacAddress, true);
+                                    Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
+                                            new FutureCallback<RpcResult<AddFlowOutput>>() {
+
+                                            @Override
+                                            public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
+                                                LOG.debug("handleEvent : Configured outbound rule, sending packet out"
+                                                        + "from {} to {}", internalAddress, externalAddress);
+                                                prepareAndSendPacketOut(naptEntryEvent, finalRouterId);
+                                            }
+
+                                            @Override
+                                            public void onFailure(@Nonnull Throwable throwable) {
+                                                LOG.error("handleEvent : Error configuring outbound "
+                                                        + "SNAT flows using RPC for SNAT connection from {} to {}",
+                                                                  internalAddress, externalAddress);
+                                            }
+                                        });
+                                }
+
+                                @Override
+                                public void onFailure(@Nonnull Throwable throwable) {
+                                    LOG.error("handleEvent : Error configuring inbound SNAT flows "
+                                            + "using RPC for SNAT connection from {} to {}",
+                                            internalAddress, externalAddress);
+                                }
+                            });
+                    String key = naptEntryEvent.getRouterId() + NatConstants.COLON_SEPARATOR
+                            + naptEntryEvent.getIpAddress() + NatConstants.COLON_SEPARATOR
+                            + naptEntryEvent.getPortNumber();
+                    NatPacketProcessingState state = NaptPacketInHandler.INCOMING_PACKET_MAP.get(key);
+                    state.setFlowInstalledTime(System.currentTimeMillis());
+                } else {
+                    prepareAndSendPacketOut(naptEntryEvent, routerId);
+                }
+                LOG.trace("handleEvent : Time elapsed after Processsing snat ({}:{}) packet: {}ms,isPktProcessed:{} ",
+                        naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(),
+                        (System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime()),
+                        naptEntryEvent.isPktProcessed());
+            } else {
+                LOG.debug("handleEvent : Inside delete Operation of NaptEventHandler");
+                removeNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId, naptEntryEvent.getIpAddress(),
+                    naptEntryEvent.getPortNumber());
+                LOG.info("handleEvent : exited for removeEvent for IP {}, port {}, routerID : {}",
+                        naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
+            }
+        } catch (Exception e) {
+            LOG.error("handleEvent :Exception in NaptEventHandler.handleEvent() payload {}", naptEntryEvent, e);
         }
-        if(naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
-            LOG.debug("NAT Service : Inside Add operation of NaptEventHandler");
+    }
 
-            //Get the external network ID from the ExternalRouter model
-            Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
-            if(networkId == null ){
-                LOG.error("NAT Service : networkId is null");
+    private void prepareAndSendPacketOut(NAPTEntryEvent naptEntryEvent, Long routerId) {
+        //Send Packetout - tcp or udp packets which got punted to controller.
+        BigInteger metadata = naptEntryEvent.getPacketReceived().getMatch().getMetadata().getMetadata();
+        byte[] inPayload = naptEntryEvent.getPacketReceived().getPayload();
+        Ethernet ethPkt = new Ethernet();
+        if (inPayload != null) {
+            try {
+                ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte);
+            } catch (PacketException e) {
+                LOG.error("prepareAndSendPacketOut : Failed to decode Packet", e);
                 return;
             }
+        }
 
-            //Get the VPN ID from the ExternalNetworks model
-            Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
-            if(vpnUuid == null ){
-                LOG.error("NAT Service : vpnUuid is null");
-                return;
-            }
-            Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
-
-            //Get the internal IpAddress, internal port number from the event
-            String internalIpAddress = naptEntryEvent.getIpAddress();
-            int internalPort = naptEntryEvent.getPortNumber();
-            SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
-            NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
-
-            //Get the external IP address for the corresponding internal IP address
-            SessionAddress externalAddress = naptManager.getExternalAddressMapping(routerId, internalAddress, naptEntryEvent.getProtocol());
-            if(externalAddress == null ){
-                if(externalAddress == null){
-                    LOG.error("NAT Service : externalAddress is null");
-                    return;
-                }
-            }
-            //Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
-            buildAndInstallNatFlows(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, vpnId, routerId, bgpVpnId, internalAddress, externalAddress, protocol);
-            buildAndInstallNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnId, routerId, bgpVpnId, externalAddress, internalAddress, protocol);
+        long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
+        LOG.debug("prepareAndSendPacketOut : portTag from incoming packet is {}", portTag);
+        String interfaceName = getInterfaceNameFromTag(portTag);
+        LOG.debug("prepareAndSendPacketOut : interfaceName fetched from portTag is {}", interfaceName);
+        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
+                .interfaces.Interface iface = null;
+        int vlanId = 0;
+        iface = interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
+        if (iface == null) {
+            LOG.error("prepareAndSendPacketOut : Unable to read interface {} from config DataStore", interfaceName);
+            return;
+        }
+        List<ActionInfo> actionInfos = new ArrayList<>();
+        IfL2vlan ifL2vlan = iface.getAugmentation(IfL2vlan.class);
+        if (ifL2vlan != null && ifL2vlan.getVlanId() != null) {
+            vlanId = ifL2vlan.getVlanId().getValue() == null ? 0 : ifL2vlan.getVlanId().getValue();
+        }
+        InterfaceInfo infInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
+        if (infInfo == null) {
+            LOG.error("prepareAndSendPacketOut : error in getting interfaceInfo from Operation DS");
+            return;
+        }
 
-        }else{
-            LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler");
-            removeNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, routerId, naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber());
+        byte[] pktOut = buildNaptPacketOut(ethPkt);
+        if (ethPkt.getEtherType() != (short) NwConstants.ETHTYPE_802_1Q) {
+            // VLAN Access port
+            LOG.debug("prepareAndSendPacketOut : vlanId is {}", vlanId);
+            if (vlanId != 0) {
+                // Push vlan
+                actionInfos.add(new ActionPushVlan(0));
+                actionInfos.add(new ActionSetFieldVlanVid(1, vlanId));
+            } else {
+                LOG.debug("prepareAndSendPacketOut : No vlanId {}, may be untagged", vlanId);
+            }
+        } else {
+            // VLAN Trunk Port
+            LOG.debug("prepareAndSendPacketOut : This is VLAN Trunk port case - need not do VLAN tagging again");
         }
+        if (pktOut != null) {
+            String routerName = NatUtil.getRouterName(dataBroker, routerId);
+            long tunId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, elanManager, idManager, routerId,
+                    routerName);
+            sendNaptPacketOut(pktOut, infInfo, actionInfos, tunId);
+        } else {
+            LOG.warn("prepareAndSendPacketOut : Unable to send Packet Out");
+        }
+    }
 
-        LOG.info("NAT Service : handleNaptEvent() exited for IP, port, routerID : {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
+    public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId,
+                                               long bgpVpnId, SessionAddress actualSourceAddress,
+                                               SessionAddress translatedSourceAddress,
+                                               NAPTEntryEvent.Protocol protocol, String extGwMacAddress) {
+        buildAndInstallNatFlowsOptionalRpc(dpnId, tableId, vpnId, routerId, bgpVpnId, actualSourceAddress,
+                translatedSourceAddress, protocol, extGwMacAddress, false);
     }
 
-    public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId, long bgpVpnId, SessionAddress actualSourceAddress,
-                                         SessionAddress translatedSourceAddress, NAPTEntryEvent.Protocol protocol){
-        LOG.debug("NAT Service : Build and install NAPT flows in InBound and OutBound tables for dpnId {} and routerId {}", dpnId, routerId);
+    public static Future<RpcResult<AddFlowOutput>> buildAndInstallNatFlowsOptionalRpc(
+            BigInteger dpnId, short tableId, long vpnId, long routerId, long bgpVpnId,
+            SessionAddress actualSourceAddress, SessionAddress translatedSourceAddress,
+            NAPTEntryEvent.Protocol protocol, String extGwMacAddress,
+            boolean sendRpc) {
+        LOG.debug("buildAndInstallNatFlowsOptionalRpc : Build and install table={} flow on dpnId {} and routerId {}",
+                tableId, dpnId, routerId);
         //Build the flow for replacing the actual IP and port with the translated IP and port.
-        String actualIp = actualSourceAddress.getIpAddress();
-        int actualPort = actualSourceAddress.getPortNumber();
-        String translatedIp = translatedSourceAddress.getIpAddress();
-        String translatedPort = String.valueOf(translatedSourceAddress.getPortNumber());
-        int idleTimeout=0;
-        if(tableId == NatConstants.OUTBOUND_NAPT_TABLE) {
+        int idleTimeout = 0;
+        if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
             idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
         }
-        long metaDataValue = routerId;
-        String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metaDataValue), actualIp, actualPort);
-
         long intranetVpnId;
-        if(bgpVpnId != NatConstants.INVALID_ID){
+        if (bgpVpnId != NatConstants.INVALID_ID) {
             intranetVpnId = bgpVpnId;
-        }else{
+        } else {
             intranetVpnId = routerId;
         }
-        LOG.debug("NAT Service : Intranet VPN ID {}", intranetVpnId);
-        LOG.debug("NAT Service : Router ID {}", routerId);
-        FlowEntity snatFlowEntity = MDSALUtil.buildFlowEntity(dpnId, tableId, switchFlowRef, NatConstants.DEFAULT_NAPT_FLOW_PRIORITY, NatConstants.NAPT_FLOW_NAME,
-                idleTimeout, 0, NatUtil.getCookieNaptFlow(metaDataValue), buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, intranetVpnId, vpnId),
-                buildAndGetSetActionInstructionInfo(translatedIp, translatedPort, intranetVpnId, vpnId, tableId, protocol));
+        LOG.debug("buildAndInstallNatFlowsOptionalRpc : Intranet VPN ID {} Router ID {}", intranetVpnId, routerId);
+        String translatedIp = translatedSourceAddress.getIpAddress();
+        int translatedPort = translatedSourceAddress.getPortNumber();
+        String actualIp = actualSourceAddress.getIpAddress();
+        int actualPort = actualSourceAddress.getPortNumber();
+        String switchFlowRef =
+            NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), actualIp, actualPort);
+
+        FlowEntity snatFlowEntity = new FlowEntityBuilder()
+            .setDpnId(dpnId)
+            .setTableId(tableId)
+            .setFlowId(switchFlowRef)
+            .setPriority(NatConstants.DEFAULT_NAPT_FLOW_PRIORITY)
+            .setFlowName(NatConstants.NAPT_FLOW_NAME)
+            .setIdleTimeOut(idleTimeout)
+            .setHardTimeOut(0)
+            .setCookie(NatUtil.getCookieNaptFlow(routerId))
+            .setMatchInfoList(buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, intranetVpnId))
+            .setInstructionInfoList(buildAndGetSetActionInstructionInfo(translatedIp, translatedPort,
+                                            intranetVpnId, vpnId, tableId, protocol, extGwMacAddress))
+            .setSendFlowRemFlag(true)
+            .build();
+
+        // Install flows using RPC to prevent race with future packet-out that depends on this flow
+        Future<RpcResult<AddFlowOutput>> addFlowResult = null;
+        if (sendRpc) {
+            Flow flow = snatFlowEntity.getFlowBuilder().build();
+            NodeRef nodeRef = getNodeRef(dpnId);
+            FlowRef flowRef = getFlowRef(dpnId, flow);
+            AddFlowInput addFlowInput = new AddFlowInputBuilder(flow).setFlowRef(flowRef).setNode(nodeRef).build();
+            long startTime = System.currentTimeMillis();
+            addFlowResult = salFlowServiceRpc.addFlow(addFlowInput);
+            LOG.debug("buildAndInstallNatFlowsOptionalRpc : Time elapsed for salFlowServiceRpc table {}: {}ms ",
+                    tableId, (System.currentTimeMillis() - startTime));
+         // Keep flow installation through MDSAL as well to be able to handle switch failures
+            startTime = System.currentTimeMillis();
+            mdsalManager.installFlow(snatFlowEntity);
+            LOG.trace("buildAndInstallNatFlowsOptionalRpc : Time Elapsed while installing table-{} "
+                    + "flow on DPN:{} for snat packet({},{}): {}ms", tableId, dpnId,
+                    actualSourceAddress.getIpAddress(),actualSourceAddress.getPortNumber(),
+                    (System.currentTimeMillis() - startTime));
+        } else {
+            long startTime = System.currentTimeMillis();
+            mdsalManager.syncInstallFlow(snatFlowEntity, 1);
+            LOG.trace("buildAndInstallNatFlowsOptionalRpc : Time Elapsed while installing table-{} "
+                    + "flow on DPN:{} for snat packet({},{}): {}ms", tableId, dpnId,
+                    actualSourceAddress.getIpAddress(),actualSourceAddress.getPortNumber(),
+                    (System.currentTimeMillis() - startTime));
+        }
+        LOG.trace("buildAndInstallNatFlowsOptionalRpc : Exited");
 
-        snatFlowEntity.setSendFlowRemFlag(true);
+        return addFlowResult;
+    }
 
-        LOG.debug("NAT Service : Installing the NAPT flow in the table {} for the switch with the DPN ID {} ", tableId, dpnId);
-        mdsalManager.installFlow(snatFlowEntity);
+    private static Node buildInventoryDpnNode(BigInteger dpnId) {
+        NodeId nodeId = new NodeId("openflow:" + dpnId);
+        Node nodeDpn = new NodeBuilder().setId(nodeId).setKey(new NodeKey(nodeId)).build();
+        return nodeDpn;
     }
 
-    private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId, NAPTEntryEvent.Protocol protocol, long segmentId, long vpnId){
+    private static NodeRef getNodeRef(BigInteger dpnId) {
+        NodeId nodeId = new NodeId("openflow:" + dpnId);
+        return new NodeRef(InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, new NodeKey(nodeId)).toInstance());
+    }
+
+    public static FlowRef getFlowRef(BigInteger dpId, Flow flow) {
+        FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
+        Node nodeDpn = buildInventoryDpnNode(dpId);
+        InstanceIdentifier<Flow> flowInstanceId =
+                InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
+                .child(Table.class, new TableKey(flow.getTableId()))
+                .child(Flow.class, flowKey)
+                .build();
+        return new FlowRef(flowInstanceId);
+    }
+
+    private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId,
+                                                        NAPTEntryEvent.Protocol protocol, long segmentId) {
         MatchInfo ipMatchInfo = null;
         MatchInfo portMatchInfo = null;
         MatchInfo protocolMatchInfo = null;
@@ -168,88 +483,179 @@ public class NaptEventHandler {
             ipAddressAsString = ipAddress.getHostAddress();
 
         } catch (UnknownHostException e) {
-            LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed  to build NAPT Flow for  ip {}", ipAddress);
+            LOG.error("buildAndGetMatchInfo : UnknowHostException in buildAndGetMatchInfo."
+                    + "Failed  to build NAPT Flow for ip {}", ipAddress, e);
             return null;
         }
 
         MatchInfo metaDataMatchInfo = null;
-        if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
-            ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_source, new String[] {ipAddressAsString, "32" });
-            if(protocol == NAPTEntryEvent.Protocol.TCP) {
-                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
-                portMatchInfo = new MatchInfo(MatchFieldType.tcp_src, new long[]{port});
-            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
-                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
-                portMatchInfo = new MatchInfo(MatchFieldType.udp_src, new long[]{port});
+        if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
+            ipMatchInfo = new MatchIpv4Source(ipAddressAsString, "32");
+            if (protocol == NAPTEntryEvent.Protocol.TCP) {
+                protocolMatchInfo = MatchIpProtocol.TCP;
+                portMatchInfo = new MatchTcpSourcePort(port);
+            } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
+                protocolMatchInfo = MatchIpProtocol.UDP;
+                portMatchInfo = new MatchUdpSourcePort(port);
             }
-            metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID});
-        }else{
-            ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_destination, new String[] {ipAddressAsString, "32" });         
-            if(protocol == NAPTEntryEvent.Protocol.TCP) {
-                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
-                portMatchInfo = new MatchInfo(MatchFieldType.tcp_dst, new long[]{port});
-            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
-                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
-                portMatchInfo = new MatchInfo(MatchFieldType.udp_dst, new long[]{port});
+            metaDataMatchInfo =
+                    new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID);
+        } else {
+            ipMatchInfo = new MatchIpv4Destination(ipAddressAsString, "32");
+            if (protocol == NAPTEntryEvent.Protocol.TCP) {
+                protocolMatchInfo = MatchIpProtocol.TCP;
+                portMatchInfo = new MatchTcpDestinationPort(port);
+            } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
+                protocolMatchInfo = MatchIpProtocol.UDP;
+                portMatchInfo = new MatchUdpDestinationPort(port);
             }
-            //metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID});
+            //metaDataMatchInfo = new MatchMetadata(BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID);
         }
         ArrayList<MatchInfo> matchInfo = new ArrayList<>();
-        matchInfo.add(new MatchInfo(MatchFieldType.eth_type, new long[] { 0x0800L }));
+        matchInfo.add(MatchEthernetType.IPV4);
         matchInfo.add(ipMatchInfo);
         matchInfo.add(protocolMatchInfo);
         matchInfo.add(portMatchInfo);
-        if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
+        if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
             matchInfo.add(metaDataMatchInfo);
         }
         return matchInfo;
     }
 
-    private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, String port, long segmentId, long vpnId, short tableId, NAPTEntryEvent.Protocol protocol) {
+    private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, int port,
+                                                                             long segmentId, long vpnId,
+                                                                             short tableId,
+                                                                             NAPTEntryEvent.Protocol protocol,
+                                                                             String extGwMacAddress) {
         ActionInfo ipActionInfo = null;
+        ActionInfo macActionInfo = null;
         ActionInfo portActionInfo = null;
         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
-
-        if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
-            ipActionInfo = new ActionInfo(ActionType.set_source_ip, new String[] {ipAddress});
-            if(protocol == NAPTEntryEvent.Protocol.TCP) {
-               portActionInfo = new ActionInfo( ActionType.set_tcp_source_port, new String[] {port});
-            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
-               portActionInfo = new ActionInfo( ActionType.set_udp_source_port, new String[] {port});
-            }
-            instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID}));
-        }else{
-            ipActionInfo = new ActionInfo(ActionType.set_destination_ip, new String[] {ipAddress});
-            if(protocol == NAPTEntryEvent.Protocol.TCP) {
-               portActionInfo = new ActionInfo( ActionType.set_tcp_destination_port, new String[] {port});
-            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
-               portActionInfo = new ActionInfo( ActionType.set_udp_destination_port, new String[] {port});
-            }
-            instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID}));
+        switch (tableId) {
+            case NwConstants.OUTBOUND_NAPT_TABLE:
+                ipActionInfo = new ActionSetSourceIp(ipAddress);
+                // Added External Gateway MAC Address
+                macActionInfo = new ActionSetFieldEthernetSource(new MacAddress(extGwMacAddress));
+                if (protocol == NAPTEntryEvent.Protocol.TCP) {
+                    portActionInfo = new ActionSetTcpSourcePort(port);
+                } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
+                    portActionInfo = new ActionSetUdpSourcePort(port);
+                }
+                // reset the split-horizon bit to allow traffic from tunnel to be sent back to the provider port
+                instructionInfo.add(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnId),
+                    MetaDataUtil.METADATA_MASK_VRFID.or(MetaDataUtil.METADATA_MASK_SH_FLAG)));
+                break;
+
+            case NwConstants.INBOUND_NAPT_TABLE:
+                ipActionInfo = new ActionSetDestinationIp(ipAddress);
+                if (protocol == NAPTEntryEvent.Protocol.TCP) {
+                    portActionInfo = new ActionSetTcpDestinationPort(port);
+                } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
+                    portActionInfo = new ActionSetUdpDestinationPort(port);
+                }
+                instructionInfo.add(new InstructionWriteMetadata(
+                        MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID));
+                break;
+
+            default:
+                LOG.error("buildAndGetSetActionInstructionInfo : Neither OUTBOUND_NAPT_TABLE nor "
+                        + "INBOUND_NAPT_TABLE matches with input table id {}", tableId);
+                return null;
         }
 
         listActionInfo.add(ipActionInfo);
         listActionInfo.add(portActionInfo);
-
-        instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
-        instructionInfo.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.NAPT_PFIB_TABLE }));
+        if (macActionInfo != null) {
+            listActionInfo.add(macActionInfo);
+            LOG.debug("buildAndGetSetActionInstructionInfo : External GW MAC Address {} is found  ", macActionInfo);
+        }
+        instructionInfo.add(new InstructionApplyActions(listActionInfo));
+        instructionInfo.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
 
         return instructionInfo;
     }
 
-    void removeNatFlows(BigInteger dpnId, short tableId ,long segmentId, String ip, int port){
-        if(dpnId == null || dpnId.equals(BigInteger.ZERO)){
-            LOG.error("NAT Service : DPN ID {} is invalid" , dpnId);
+    void removeNatFlows(BigInteger dpnId, short tableId ,long segmentId, String ip, int port) {
+        if (dpnId == null || dpnId.equals(BigInteger.ZERO)) {
+            LOG.error("removeNatFlows : DPN ID {} is invalid" , dpnId);
+            return;
         }
-        LOG.debug("NAT Service : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ", dpnId, segmentId, ip, port);
+        LOG.debug("removeNatFlows : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ",
+            dpnId, segmentId, ip, port);
 
         //Build the flow with the port IP and port as the match info.
         String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(segmentId), ip, port);
         FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
-        LOG.debug("NAT Service : Remove the flow in the table {} for the switch with the DPN ID {}", NatConstants.INBOUND_NAPT_TABLE, dpnId);
+        LOG.debug("removeNatFlows : Remove the flow in the table {} for the switch with the DPN ID {}",
+            NwConstants.INBOUND_NAPT_TABLE, dpnId);
+        long startTime = System.currentTimeMillis();
         mdsalManager.removeFlow(snatFlowEntity);
+        LOG.trace("removeNatFlows : Time Elapsed for removing table-{} flow from switch with DPN ID:{} "
+                + "for SNAT ({}:{}) session:{}ms", tableId, dpnId, ip, port, (System.currentTimeMillis() - startTime));
+    }
+
+    protected byte[] buildNaptPacketOut(Ethernet etherPkt) {
+        LOG.debug("removeNatFlows : About to build Napt Packet Out");
+        if (etherPkt.getPayload() instanceof IPv4) {
+            byte[] rawPkt;
+            IPv4 ipPkt = (IPv4) etherPkt.getPayload();
+            if (ipPkt.getPayload() instanceof TCP || ipPkt.getPayload() instanceof UDP) {
+                try {
+                    rawPkt = etherPkt.serialize();
+                    return rawPkt;
+                } catch (PacketException e2) {
+                    LOG.error("failed to build NAPT Packet out ", e2);
+                    return null;
+                }
+            } else {
+                LOG.error("removeNatFlows : Unable to build NaptPacketOut since its neither TCP nor UDP");
+                return null;
+            }
+        }
+        LOG.error("removeNatFlows : Unable to build NaptPacketOut since its not IPv4 packet");
+        return null;
+    }
 
+    private void sendNaptPacketOut(byte[] pktOut, InterfaceInfo infInfo, List<ActionInfo> actionInfos, Long tunId) {
+        LOG.trace("sendNaptPacketOut: Sending packet out DpId {}, interfaceInfo {}", infInfo.getDpId(), infInfo);
+        // set inPort, and action as OFPP_TABLE so that it starts from table 0 (lowest table as per spec)
+        actionInfos.add(new ActionSetFieldTunnelId(2, BigInteger.valueOf(tunId)));
+        actionInfos.add(new ActionOutput(3, new Uri("0xfffffff9")));
+        NodeConnectorRef inPort = MDSALUtil.getNodeConnRef(infInfo.getDpId(), String.valueOf(infInfo.getPortNo()));
+        LOG.debug("sendNaptPacketOut : inPort for packetout is being set to {}", String.valueOf(infInfo.getPortNo()));
+        TransmitPacketInput output = MDSALUtil.getPacketOut(actionInfos, pktOut, infInfo.getDpId().longValue(), inPort);
+        LOG.debug("sendNaptPacketOut : Transmitting packet: {}, inPort {}", output, inPort);
+        this.pktService.transmitPacket(output);
     }
 
+    private String getInterfaceNameFromTag(long portTag) {
+        String interfaceName = null;
+        GetInterfaceFromIfIndexInput input =
+            new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
+        Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
+            interfaceManagerRpc.getInterfaceFromIfIndex(input);
+        try {
+            GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
+            interfaceName = output.getInterfaceName();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("getInterfaceNameFromTag : Error while retrieving the interfaceName from tag using "
+                + "getInterfaceFromIfIndex RPC");
+        }
+        LOG.trace("getInterfaceNameFromTag : Returning interfaceName {} for tag {} form getInterfaceNameFromTag",
+            interfaceName, portTag);
+        return interfaceName;
+    }
+
+    private long getVpnIdFromExternalSubnet(DataBroker dataBroker, Long routerId, String externalIpAddress) {
+        String routerName = NatUtil.getRouterName(dataBroker, routerId);
+        if (routerName != null) {
+            Routers extRouter = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
+            if (extRouter != null) {
+                return NatUtil.getExternalSubnetVpnIdForRouterExternalIp(dataBroker, externalIpAddress, extRouter);
+            }
+        }
+
+        return NatConstants.INVALID_ID;
+    }
 }