Bug 7447: Unexpected flows from T21 to T44 for FIP
[netvirt.git] / vpnservice / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / internal / ExternalRoutersListener.java
index 1d2656e9b2fbc071162e6ff4ca3010fb6e3d6e9e..11e93f7d590715b1c38930d62e19e361d6ff9d55 100644 (file)
@@ -5,46 +5,29 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.netvirt.natservice.internal;
 
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.math.BigInteger;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
-
-import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalIpsCounter;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.IntextIpPortMap;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.ExternalCounters;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounter;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.*;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMapping;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
@@ -58,44 +41,70 @@ import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.MatchFieldType;
 import org.opendaylight.genius.mdsalutil.MatchInfo;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
-import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
 import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
+import org.opendaylight.netvirt.fibmanager.api.IFibManager;
+import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
+import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExtRouters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalIpsCounter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.IntextIpPortMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.RouterIdName;
 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.RoutersKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.ExternalCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounter;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.router.id.name.RouterIds;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.router.id.name.RouterIdsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.router.id.name.RouterIdsKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -103,94 +112,69 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
-import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.JdkFutureAdapters;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * Created by EYUGSAR on 2/20/2016.
- */
-
-public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Routers, ExternalRoutersListener>{
-
+public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Routers, ExternalRoutersListener> {
     private static final Logger LOG = LoggerFactory.getLogger( ExternalRoutersListener.class);
-    private static long label;
     private ListenerRegistration<DataChangeListener> listenerRegistration;
     private final DataBroker dataBroker;
-    private IMdsalApiManager mdsalManager;
-    private ItmRpcService itmManager;
-    private OdlInterfaceRpcService interfaceManager;
-    private IdManagerService idManager;
-    private NaptManager naptManager;
-    private NAPTSwitchSelector naptSwitchSelector;
-    private IBgpManager bgpManager;
-    private VpnRpcService vpnService;
-    private FibRpcService fibService;
-    private SNATDefaultRouteProgrammer defaultRouteProgrammer;
+    private final IMdsalApiManager mdsalManager;
+    private final ItmRpcService itmManager;
+    private final OdlInterfaceRpcService interfaceManager;
+    private final IdManagerService idManager;
+    private final NaptManager naptManager;
+    private final NAPTSwitchSelector naptSwitchSelector;
+    private final IBgpManager bgpManager;
+    private final VpnRpcService vpnService;
+    private final FibRpcService fibService;
+    private final SNATDefaultRouteProgrammer defaultRouteProgrammer;
+    private final NaptEventHandler naptEventHandler;
+    private final NaptPacketInHandler naptPacketInHandler;
+    private final IFibManager fibManager;
+    private final IVpnManager vpnManager;
     private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
     static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000022", 16);
-    private NaptEventHandler naptEventHandler;
-    private NaptPacketInHandler naptPacketInHandler;
-
-    public void setNaptEventHandler(NaptEventHandler naptEventHandler) {
-        this.naptEventHandler = naptEventHandler;
-    }
 
-    public void setNaptPacketInHandler(NaptPacketInHandler naptPacketInHandler) {
-        this.naptPacketInHandler = naptPacketInHandler;
-    }
-
-    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+    public ExternalRoutersListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
+                                   final ItmRpcService itmManager,
+                                   final OdlInterfaceRpcService interfaceManager,
+                                   final IdManagerService idManager,
+                                   final NaptManager naptManager,
+                                   final NAPTSwitchSelector naptSwitchSelector,
+                                   final IBgpManager bgpManager,
+                                   final VpnRpcService vpnService,
+                                   final FibRpcService fibService,
+                                   final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
+                                   final NaptEventHandler naptEventHandler,
+                                   final NaptPacketInHandler naptPacketInHandler,
+                                   final IFibManager fibManager,
+                                   final IVpnManager vpnManager) {
+        super(Routers.class, ExternalRoutersListener.class);
+        this.dataBroker = dataBroker;
         this.mdsalManager = mdsalManager;
-    }
-
-    public void setItmManager(ItmRpcService itmManager) {
         this.itmManager = itmManager;
-    }
-
-    public void setIdManager(IdManagerService idManager) {
-        this.idManager = idManager;
-        createGroupIdPool();
-    }
-
-    void setDefaultProgrammer(SNATDefaultRouteProgrammer defaultRouteProgrammer) {
-        this.defaultRouteProgrammer = defaultRouteProgrammer;
-    }
-
-
-    public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
         this.interfaceManager = interfaceManager;
-    }
-
-    public void setNaptManager(NaptManager naptManager) {
+        this.idManager = idManager;
         this.naptManager = naptManager;
-    }
-
-    public void setNaptSwitchSelector(NAPTSwitchSelector naptSwitchSelector) {
         this.naptSwitchSelector = naptSwitchSelector;
-    }
-
-    public void setBgpManager(IBgpManager bgpManager) {
         this.bgpManager = bgpManager;
-    }
-
-    public void setVpnService(VpnRpcService vpnService) {
         this.vpnService = vpnService;
+        this.fibService = fibService;
+        this.defaultRouteProgrammer = snatDefaultRouteProgrammer;
+        this.naptEventHandler = naptEventHandler;
+        this.naptPacketInHandler = naptPacketInHandler;
+        this.fibManager = fibManager;
+        this.vpnManager = vpnManager;
     }
 
-    public void setFibService(FibRpcService fibService) {
-        this.fibService = fibService;
+    @Override
+    public void init() {
+        LOG.info("{} init", getClass().getSimpleName());
+        registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
+        createGroupIdPool();
     }
 
-    public ExternalRoutersListener(DataBroker dataBroker )
-    {
-        super( Routers.class, ExternalRoutersListener.class );
-        this.dataBroker = dataBroker;
+    @Override
+    protected InstanceIdentifier<Routers> getWildCardPath() {
+        return InstanceIdentifier.create(ExtRouters.class).child(Routers.class);
     }
 
     @Override
@@ -200,9 +184,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
         // Populate the router-id-name container
         String routerName = routers.getRouterName();
-        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
-        RouterIds rtrs = new RouterIdsBuilder().setKey(new RouterIdsKey(routerId)).setRouterId(routerId).setRouterName(routerName).build();
-        MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.CONFIGURATION, getRoutersIdentifier(routerId), rtrs);
+        NatUtil.createRouterIdsConfigDS(dataBroker, routerName);
 
         LOG.info("NAT Service : Installing NAT default route on all dpns part of router {}", routers.getRouterName());
         try {
@@ -211,35 +193,30 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
             LOG.debug("NAT Service : Exception {} while Installing NAT default route on all dpns part of router {}",ex,routers.getRouterName());
         }
 
+        long segmentId = NatUtil.getVpnId(dataBroker, routerName);
+        // Allocate Primary Napt Switch for this router
+        BigInteger primarySwitchId = getPrimaryNaptSwitch(routerName, segmentId);
+        if(primarySwitchId == null || primarySwitchId.equals(BigInteger.ZERO)){
+            LOG.error("NAT Service: Failed to get or allocated NAPT switch");
+            return;
+        }
+
+        handleRouterGwFlows(routers, primarySwitchId, NwConstants.ADD_FLOW);
         if( !routers.isEnableSnat()) {
-            LOG.info("NAT Service : SNAT is disabled for external router {} ", routers.getRouterName());
+            LOG.info("NAT Service : SNAT is disabled for external router {} ", routerName);
             return;
         }
 
-        handleEnableSnat(routers);
+        handleEnableSnat(routers, segmentId, primarySwitchId);
     }
 
-    public void handleEnableSnat(Routers routers){
+    public void handleEnableSnat(Routers routers, long segmentId, BigInteger primarySwitchId) {
         String routerName = routers.getRouterName();
         LOG.info("NAT Service : Handling SNAT for router {}", routerName);
 
-        long segmentId = NatUtil.getVpnId(dataBroker, routerName);
         naptManager.initialiseExternalCounter(routers, segmentId);
-
         subnetRegisterMapping(routers,segmentId);
 
-        // Allocate Primary Napt Switch for this router
-        BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterId(dataBroker,segmentId);
-        if (primarySwitchId != null && !primarySwitchId.equals(BigInteger.ZERO)) {
-            LOG.debug("NAT Service : Primary NAPT switch with DPN ID {} is already elected for router",primarySwitchId,routerName);
-            return;
-        }
-        primarySwitchId = naptSwitchSelector.selectNewNAPTSwitch(routerName);
-        LOG.debug("NAT Service : Primary NAPT switch DPN ID {}", primarySwitchId);
-        if(primarySwitchId == null || primarySwitchId.equals(BigInteger.ZERO)){
-            LOG.error("NAT Service : Unable to to select the primary NAPT switch");
-            return;
-        }
         LOG.debug("NAT Service : About to create and install outbound miss entry in Primary Switch {} for router {}", primarySwitchId, routerName);
 
         long bgpVpnId = NatConstants.INVALID_ID;
@@ -247,7 +224,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         if (bgpVpnUuid != null) {
             bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
         }
-        if (bgpVpnId != NatConstants.INVALID_ID){           
+        if (bgpVpnId != NatConstants.INVALID_ID){
             installFlowsWithUpdatedVpnId(primarySwitchId, routerName, bgpVpnId, segmentId, false);
         } else {
             // write metadata and punt
@@ -263,6 +240,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     } else {
                         LOG.debug("NAT Service : Handle NAPT switch");
                         handlePrimaryNaptSwitch(dpnId, routerName);
+                        installNaptPfibExternalOutputFlow(routers, dpnId);
                     }
                 }
             }
@@ -372,7 +350,67 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         LOG.info("NAT Service : handleEnableSnat() Exit");
     }
 
-    private void subnetRegisterMapping(Routers routerEntry,Long segmentId) {
+    private BigInteger getPrimaryNaptSwitch(String routerName, long segmentId) {
+        // Allocate Primary Napt Switch for this router
+        BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
+        if (primarySwitchId != null && !primarySwitchId.equals(BigInteger.ZERO)) {
+            LOG.debug("NAT Service : Primary NAPT switch with DPN ID {} is already elected for router",primarySwitchId,routerName);
+            return primarySwitchId;
+        }
+
+        primarySwitchId = naptSwitchSelector.selectNewNAPTSwitch(routerName);
+        LOG.debug("NAT Service : Primary NAPT switch DPN ID {}", primarySwitchId);
+        if(primarySwitchId == null || primarySwitchId.equals(BigInteger.ZERO)){
+            LOG.error("NAT Service : Unable to to select the primary NAPT switch");
+        }
+
+        return primarySwitchId;
+    }
+
+    private void installNaptPfibExternalOutputFlow(Routers routers, BigInteger dpnId) {
+        Long extVpnId = NatUtil.getVpnId(dataBroker, routers.getNetworkId().getValue());
+        List<String> extIps = routers.getExternalIps();
+        installNaptPfibExternalOutputFlow(dpnId, extVpnId, extIps);
+    }
+
+    protected void installNaptPfibExternalOutputFlow(String routerName, Long routerId, BigInteger dpnId) {
+        Long extVpnId = NatUtil.getVpnId(dataBroker, routerId);
+        if (extVpnId == null || extVpnId == NatConstants.INVALID_ID) {
+            LOG.debug("installNaptPfibExternalOutputFlow - not found extVpnId for router {}", routerId);
+            extVpnId = routerId;
+        }
+        List<String> externalIps = NatUtil.getExternalIpsFromRouter(dataBroker, routerName);
+        installNaptPfibExternalOutputFlow(dpnId, extVpnId, externalIps);
+    }
+
+    private void installNaptPfibExternalOutputFlow(BigInteger dpnId, Long extVpnId, List<String> externalIps) {
+        if (externalIps == null || externalIps.isEmpty()) {
+            LOG.debug("installNaptPfibExternalOutputFlow - empty external Ips list for dpnId {} extVpnId {}",
+                    dpnId, extVpnId);
+            return;
+        }
+        for (String ip : externalIps) {
+            Uuid subnetId = getSubnetIdForFixedIp(ip);
+            if (subnetId != null) {
+                LOG.debug("installNaptPfibExternalOutputFlow - dpnId {} extVpnId {} subnetId {} ip {}",
+                        dpnId, extVpnId, subnetId, ip);
+                FlowEntity postNaptFlowEntity = buildNaptFibExternalOutputFlowEntity(dpnId, extVpnId, subnetId, ip);
+                mdsalManager.installFlow(postNaptFlowEntity);
+            }
+        }
+    }
+
+    private Uuid getSubnetIdForFixedIp(String ip) {
+        if (ip != null) {
+            IpAddress externalIpv4Address = new IpAddress(new Ipv4Address(ip));
+            Port port = NatUtil.getNeutronPortForRouterGetewayIp(dataBroker, externalIpv4Address);
+            Uuid subnetId = NatUtil.getSubnetIdForFloatingIp(port, externalIpv4Address);
+            return subnetId;
+        }
+        return null;
+    }
+
+    protected void subnetRegisterMapping(Routers routerEntry,Long segmentId) {
         List<Uuid> subnetList = null;
         List<String> externalIps = null;
         LOG.debug("NAT Service : Fetching values from extRouters model");
@@ -525,6 +563,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         return result;
     }
 
+    @Override
     public void close() throws Exception
     {
         if (listenerRegistration != null)
@@ -559,8 +598,14 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                 append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
     }
 
+    private String getFlowRefNaptFib(BigInteger dpnId, short tableId, long routerID, String externalIp) {
+        return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+                append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).
+                append(NatConstants.FLOWID_SEPARATOR).append(externalIp).toString();
+    }
+
     public BigInteger getCookieOutboundFlow(long routerId) {
-        return NatConstants.COOKIE_OUTBOUND_NAPT_TABLE.add(new BigInteger("0110001", 16)).add(
+        return NwConstants.COOKIE_OUTBOUND_NAPT_TABLE.add(new BigInteger("0110001", 16)).add(
                 BigInteger.valueOf(routerId));
     }
 
@@ -570,17 +615,18 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[] { 0x0800L }));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+                MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
 
         List<InstructionInfo> instructions = new ArrayList<>();
         List<ActionInfo> actionsInfos = new ArrayList<>();
         actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, new String[] {}));
         instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
-        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+        instructions.add(new InstructionInfo(InstructionType.write_metadata,
+                new BigInteger[] { MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
 
-        String flowRef = getFlowRefOutbound(dpId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
+        String flowRef = getFlowRefOutbound(dpId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
         BigInteger cookie = getCookieOutboundFlow(routerId);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.OUTBOUND_NAPT_TABLE, flowRef,
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,
                 5, flowRef, 0, 0,
                 cookie, matches, instructions);
         LOG.debug("NAT Service : returning flowEntity {}", flowEntity);
@@ -628,44 +674,8 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         return null;
     }
 
-    protected List<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
-        LOG.debug("NAT Service : getEgressActionsForInterface called for interface {}", ifName);
-        List<ActionInfo> listActionInfo = new ArrayList<>();
-        try {
-            Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
-                interfaceManager.getEgressActionsForInterface(
-                    new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
-            RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
-            if(!rpcResult.isSuccessful()) {
-                LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", ifName, rpcResult.getErrors());
-            } else {
-                List<Action> actions =
-                    rpcResult.getResult().getAction();
-                for (Action action : actions) {
-                    org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
-                    if (actionClass instanceof OutputActionCase) {
-                        listActionInfo.add(new ActionInfo(ActionType.output,
-                                                          new String[] {((OutputActionCase)actionClass).getOutputAction()
-                                                                            .getOutputNodeConnector().getValue()}));
-                    } else if (actionClass instanceof PushVlanActionCase) {
-                        listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
-                    } else if (actionClass instanceof SetFieldCase) {
-                        if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
-                            int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch().getVlanId().getVlanId().getValue();
-                            listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
-                                                              new String[] { Long.toString(vlanVid) }));
-                        }
-                    }
-                }
-            }
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.warn("Exception when egress actions for interface {}", ifName, e);
-        }
-        return listActionInfo;
-    }
-
     protected void installSnatMissEntryForPrimrySwch(BigInteger dpnId, String routerName) {
-        LOG.debug("NAT Service : installSnatMissEntry called for for the primary NAOT switch dpnId {} ", dpnId);
+        LOG.debug("NAT Service : installSnatMissEntry called for for the primary NAPT switch dpnId {} ", dpnId);
         // Install miss entry pointing to group
         FlowEntity flowEntity = buildSnatFlowEntityForPrmrySwtch(dpnId, routerName);
         mdsalManager.installFlow(flowEntity);
@@ -699,7 +709,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[] { 0x0800L }));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+                MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
 
 
         List<InstructionInfo> instructions = new ArrayList<>();
@@ -710,11 +720,11 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         actionsInfo.add(actionSetField);
         LOG.debug("NAT Service : Setting the tunnel to the list of action infos {}", actionsInfo);
         actionsInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
-        instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfo));
-        String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfo));
+        String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
 
         LOG.debug("NAT Service : Returning SNAT Flow Entity {}", flowEntity);
         return flowEntity;
@@ -729,16 +739,16 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[] { 0x0800L }));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+                MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
 
         List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]
-                { NatConstants.OUTBOUND_NAPT_TABLE }));
+                { NwConstants.OUTBOUND_NAPT_TABLE }));
 
-        String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+        String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
 
         LOG.debug("NAT Service : Returning SNAT Flow Entity {}", flowEntity);
         return flowEntity;
@@ -762,13 +772,13 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]
-                { routerId, MetaDataUtil.METADATA_MASK_VRFID }));
+                { MetaDataUtil.getVpnIdMetadata(routerId.longValue()), MetaDataUtil.METADATA_MASK_VRFID }));
         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]
-                { NatConstants.OUTBOUND_NAPT_TABLE }));
-        String flowRef = getFlowRefTs(dpId, NatConstants.TERMINATING_SERVICE_TABLE, routerId.longValue());
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.TERMINATING_SERVICE_TABLE, flowRef,
+                { NwConstants.OUTBOUND_NAPT_TABLE }));
+        String flowRef = getFlowRefTs(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue());
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
                 NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_TS_TABLE, matches, instructions);
+                NwConstants.COOKIE_TS_TABLE, matches, instructions);
         return flowEntity;
     }
 
@@ -828,7 +838,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
         if(ifNamePrimary != null) {
             LOG.debug("NAT Service : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
-            listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
+            listActionInfoPrimary = NatUtil.getEgressActionsForInterface(interfaceManager, ifNamePrimary, routerId);
         }
         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
 
@@ -844,7 +854,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
         if (ifNamePrimary != null) {
             LOG.debug("NAT Service : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
-            listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
+            listActionInfoPrimary = NatUtil.getEgressActionsForInterface(interfaceManager, ifNamePrimary, routerId);
         }
         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
 
@@ -883,7 +893,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
     List<BucketInfo> getBucketInfoForPrimaryNaptSwitch(){
         List<BucketInfo> listBucketInfo = new ArrayList<>();
         List<ActionInfo> listActionInfoPrimary =  new ArrayList<>();
-        listActionInfoPrimary.add(new ActionInfo(ActionType.nx_resubmit, new String[]{String.valueOf(NatConstants.TERMINATING_SERVICE_TABLE)}));
+        listActionInfoPrimary.add(new ActionInfo(ActionType.nx_resubmit, new String[]{String.valueOf(NwConstants.INTERNAL_TUNNEL_TABLE)}));
         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
         listBucketInfo.add(0, bucketPrimary);
         return listBucketInfo;
@@ -902,17 +912,18 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[] { 0x0800L }));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID }));
+                MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID }));
 
         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
-        listActionInfo.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) }));
+        listActionInfo.add(new ActionInfo(ActionType.nx_load_in_port, new BigInteger[]{BigInteger.ZERO}));
+        listActionInfo.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NwConstants.L3_FIB_TABLE) }));
         instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
 
-        String flowRef = getFlowRefTs(dpId, NatConstants.NAPT_PFIB_TABLE, segmentId);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.NAPT_PFIB_TABLE, flowRef,
+        String flowRef = getFlowRefTs(dpId, NwConstants.NAPT_PFIB_TABLE, segmentId);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.NAPT_PFIB_TABLE, flowRef,
                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
 
         LOG.debug("NAT Service : Returning NaptPFib Flow Entity {}", flowEntity);
         return flowEntity;
@@ -931,7 +942,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     networkId, externalIp, routerId);
             return;
         }
-        advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
+        advToBgpAndInstallFibAndTsFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
         LOG.debug("NAT Service : handleSnatReverseTraffic() exit for DPN ID, routerId, externalIp : {}", dpnId, routerId, externalIp);
     }
 
@@ -960,7 +971,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                         for (IpMap dbIpMap : dbIpMaps) {
                             String dbExternalIp = dbIpMap.getExternalIp();
                             //Select the IPMap, whose external IP is the IP for which FIB is installed
-                            if (externalIp.equals(dbExternalIp)) {
+                            if (dbExternalIp.contains(externalIp)) {
                                 String dbInternalIp = dbIpMap.getInternalIp();
                                 IpMapKey dbIpMapKey = dbIpMap.getKey();
                                 LOG.debug("Setting label {} for internalIp {} and externalIp {}", label, dbInternalIp, externalIp);
@@ -969,9 +980,9 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                                 externalIpInDsFlag++;
                             }
                         }
-                        if (externalIpInDsFlag <=0) {
-                            LOG.debug("NAT Service : External Ip {} not found in DS,Failed to update label {} for routerId {} in DS", externalIp, label, routerId);
-                            String errMsg = String.format("Failed to update label %s due to external Ip %s not found in DS for router %s", externalIp, label, routerId);
+                        if (externalIpInDsFlag <= 0) {
+                            LOG.debug("NAT Service : External Ip {} not found in DS, Failed to update label {} for routerId {} in DS", externalIp, label, routerId);
+                            String errMsg = String.format("Failed to update label %s due to external Ip %s not found in DS for router %s", label, externalIp, routerId);
                             return Futures.immediateFailedFuture(new Exception(errMsg));
                         }
                     } else {
@@ -982,7 +993,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     //Inform BGP
                     String rd = NatUtil.getVpnRd(dataBroker, vpnName);
                     String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
-                    NatUtil.addPrefixToBGP(bgpManager, rd, externalIp, nextHopIp, label, log);
+                    NatUtil.addPrefixToBGP(dataBroker, bgpManager, fibManager, rd, externalIp, nextHopIp, label, log, RouteOrigin.STATIC);
 
                     //Install custom FIB routes
                     List<Instruction> customInstructions = new ArrayList<>();
@@ -990,8 +1001,9 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     makeTunnelTableEntry(dpnId, label, customInstructions);
                     makeLFibTableEntry(dpnId, label, tableId);
 
+                    String fibExternalIp = externalIp.contains("/32") ? externalIp : (externalIp + "/32");
                     CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId)
-                            .setIpAddress(externalIp).setServiceId(label).setInstruction(customInstructions).build();
+                            .setIpAddress(fibExternalIp).setServiceId(label).setInstruction(customInstructions).build();
                     Future<RpcResult<Void>> future = fibService.createFibEntry(input);
                     return JdkFutureAdapters.listenInPoolThread(future);
                 } else {
@@ -1075,7 +1087,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
     protected void update(InstanceIdentifier<Routers> identifier, Routers original, Routers update) {
         String routerName = original.getRouterName();
         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
-        BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
+        BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
         Uuid networkId = original.getNetworkId();
 
         // Check if its update on SNAT flag
@@ -1092,13 +1104,24 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     return;
                 }
                 List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker,routerId);
-                handleDisableSnat(routerName, networkUuid, externalIps, false, null);
+                handleDisableSnat(original, networkUuid, externalIps, false, null, dpnId);
             } else {
+                // Allocate Primary Napt Switch for existing router
+                BigInteger primarySwitchId = getPrimaryNaptSwitch(routerName, routerId);
+                if(primarySwitchId == null || primarySwitchId.equals(BigInteger.ZERO)){
+                    LOG.error("NAT Service: Failed to get or allocated NAPT switch in ExternalRouterListener.Update()");
+                    return;
+                }
                 LOG.info("NAT Service : SNAT enabled for Router {}", original.getRouterName());
-                handleEnableSnat(original);
+                handleEnableSnat(original, routerId, primarySwitchId);
             }
         }
 
+        if (!Objects.equals(original.getExtGwMacAddress(), update.getExtGwMacAddress())) {
+            handleRouterGwFlows(original, dpnId, NwConstants.DEL_FLOW);
+            handleRouterGwFlows(update, dpnId, NwConstants.ADD_FLOW);
+        }
+
         //Check if the Update is on External IPs
         LOG.debug("NAT Service : Checking if this is update on External IPs");
         List<String> originalExternalIpsList = original.getExternalIps();
@@ -1110,6 +1133,9 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         SetView<String> addedExternalIps = Sets.difference(updatedExternalIps, originalExternalIps);
         if(addedExternalIps.size() != 0) {
             LOG.debug("NAT Service : Start processing of the External IPs addition during the update operation");
+            vpnManager.setupArpResponderFlowsToExternalNetworkIps(routerName, addedExternalIps, update.getExtGwMacAddress(), dpnId,
+                    update.getNetworkId(), null, NwConstants.ADD_FLOW);
+
             for (String addedExternalIp : addedExternalIps) {
                 /*
                     1) Do nothing in the IntExtIp model.
@@ -1130,6 +1156,9 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         SetView<String> removedExternalIps = Sets.difference(originalExternalIps, updatedExternalIps);
         if(removedExternalIps.size() > 0) {
             LOG.debug("NAT Service : Start processing of the External IPs removal during the update operation");
+            vpnManager.setupArpResponderFlowsToExternalNetworkIps(routerName, removedExternalIps, original.getExtGwMacAddress(),
+                    dpnId, networkId, null, NwConstants.DEL_FLOW);
+
             List<String> removedExternalIpsAsList = new ArrayList<>();
             for (String removedExternalIp : removedExternalIps) {
              /*
@@ -1253,7 +1282,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                 LOG.debug("Remove the NAPT translation entries from Inbound NAPT tables for the removed external IP {}", externalIp);
                 for(Integer externalPort : externalPorts) {
                     //Remove the NAPT translation entries from Inbound NAPT table
-                    naptEventHandler.removeNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, routerId, externalIp, externalPort);
+                    naptEventHandler.removeNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId, externalIp, externalPort);
                 }
 
                 Set<Map.Entry<String, List<String>>> internalIpPorts = internalIpPortMap.entrySet();
@@ -1263,7 +1292,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     List<String> internalPorts = internalIpPort.getValue();
                     for(String internalPort : internalPorts){
                         //Remove the NAPT translation entries from Outbound NAPT table
-                        naptEventHandler.removeNatFlows(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, Integer.valueOf(internalPort));
+                        naptEventHandler.removeNatFlows(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, Integer.valueOf(internalPort));
                     }
                 }
             }
@@ -1332,7 +1361,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
             LOG.debug("NAT Service : End processing of the Subnet IDs removal during the update operation");
         }
     }
-    
+
     private boolean isExternalIpAllocated(String externalIp) {
         InstanceIdentifier<ExternalIpsCounter> id = InstanceIdentifier.builder(ExternalIpsCounter.class).build();
         Optional <ExternalIpsCounter> externalCountersData = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
@@ -1392,13 +1421,13 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     LOG.debug("Best effort for getting primary napt switch when router i/f are added after gateway-set");
                     dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker,routerId);
                 }
-                advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, routerId,
+                advToBgpAndInstallFibAndTsFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnName, routerId,
                         leastLoadedExtIp + "/" + leastLoadedExtIpPrefix, vpnService, fibService, bgpManager, dataBroker, LOG);
             }
         }
     }
 
-    private Long checkExternalIpLabel(long routerId, String externalIp){
+    protected Long checkExternalIpLabel(long routerId, String externalIp){
         List<IpMap> ipMaps = naptManager.getIpMapList(dataBroker, routerId);
         for(IpMap ipMap : ipMaps){
             if(ipMap.getExternalIp().equals(externalIp)){
@@ -1441,35 +1470,34 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                 LOG.error("NAT Service : Invalid routerId returned for routerName {}", routerName);
                 return;
             }
+
+            BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
+            handleRouterGwFlows(router, primarySwitchId, NwConstants.DEL_FLOW);
             List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
-            handleDisableSnat(routerName, networkUuid, externalIps, true, null);
+            handleDisableSnat(router, networkUuid, externalIps, true, null, primarySwitchId);
         }
     }
 
-    public void handleDisableSnat(String routerName, Uuid networkUuid, List<String> externalIps, boolean routerFlag, String vpnId){
+    private void handleRouterGwFlows(Routers router, BigInteger primarySwitchId, int addOrRemove) {
+        WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+        vpnManager.setupRouterGwMacFlow(router.getRouterName(), router.getExtGwMacAddress(), primarySwitchId,
+                router.getNetworkId(), writeTx, addOrRemove);
+        vpnManager.setupArpResponderFlowsToExternalNetworkIps(router.getRouterName(), router.getExternalIps(),
+                router.getExtGwMacAddress(), primarySwitchId, router.getNetworkId(), writeTx, addOrRemove);
+        writeTx.submit();
+    }
+
+    public void handleDisableSnat(Routers router, Uuid networkUuid, List<String> externalIps, boolean routerFlag,
+            String vpnId, BigInteger naptSwitchDpnId) {
         LOG.info("NAT Service : handleDisableSnat() Entry");
+        String routerName = router.getRouterName();
         try {
             Long routerId = NatUtil.getVpnId(dataBroker, routerName);
-
-            BigInteger naptSwitchDpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
-            LOG.debug("NAT Service : got primarySwitch as dpnId{} ", naptSwitchDpnId);
-            if (naptSwitchDpnId == null || naptSwitchDpnId.equals(BigInteger.ZERO)){
-                LOG.error("NAT Service : Unable to retrieve the primary NAPT switch for the router ID {} from RouterNaptSwitch model", routerId);
-                return;
-            }
-            removeNaptFlowsFromActiveSwitch(routerId, routerName, naptSwitchDpnId, networkUuid, vpnId );
-            removeFlowsFromNonActiveSwitches(routerName, naptSwitchDpnId, networkUuid);
-            try {
-                clrRtsFromBgpAndDelFibTs(naptSwitchDpnId, routerId, networkUuid, externalIps, vpnId);
-            } catch (Exception ex) {
-                LOG.debug("Failed to remove fib entries for routerId {} in naptSwitchDpnId {} : {}", routerId, naptSwitchDpnId,ex);
-            }
-
             //Use the NaptMananager removeMapping API to remove the entire list of IP addresses maintained for the router ID.
             LOG.debug("NAT Service : Remove the Internal to external IP address maintained for the router ID {} in the DS", routerId);
             naptManager.removeMapping(routerId);
 
-            if(routerFlag) {
+            if (routerFlag) {
                 removeNaptSwitch(routerName);
             } else {
                 updateNaptSwitch(routerName, BigInteger.ZERO);
@@ -1477,6 +1505,20 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
             LOG.debug("NAT Service : Remove the ExternalCounter model for the router ID {}", routerId);
             naptManager.removeExternalCounter(routerId);
+
+            LOG.debug("NAT Service : got primarySwitch as dpnId {}", naptSwitchDpnId);
+            if (naptSwitchDpnId == null || naptSwitchDpnId.equals(BigInteger.ZERO)) {
+                LOG.error("NAT Service : Unable to retrieve the primary NAPT switch for the router ID {} from RouterNaptSwitch model", routerId);
+                return;
+            }
+            removeNaptFlowsFromActiveSwitch(routerId, routerName, naptSwitchDpnId, networkUuid, vpnId, externalIps);
+            removeFlowsFromNonActiveSwitches(routerName, naptSwitchDpnId, networkUuid);
+            try {
+                clrRtsFromBgpAndDelFibTs(naptSwitchDpnId, routerId, networkUuid, externalIps, vpnId);
+            } catch (Exception ex) {
+                LOG.debug("Failed to remove fib entries for routerId {} in naptSwitchDpnId {} : {}", routerId, naptSwitchDpnId,ex);
+            }
+
         } catch (Exception ex) {
             LOG.error("Exception while handling disableSNAT : {}", ex);
         }
@@ -1528,39 +1570,41 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
     }
 
-     public void removeNaptFlowsFromActiveSwitch(long routerId, String routerName, BigInteger dpnId, Uuid networkId, String vpnName){
+     public void removeNaptFlowsFromActiveSwitch(long routerId, String routerName, BigInteger dpnId, Uuid networkId, String vpnName, List<String> externalIps){
 
         LOG.debug("NAT Service : Remove NAPT flows from Active switch");
         BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
 
         //Remove the PSNAT entry which forwards the packet to Outbound NAPT Table (For the
         // traffic which comes from the  VMs of the NAPT switches)
-        String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, routerName);
-        FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef);
+        String pSNatFlowRef = getFlowRefSnat(dpnId, NwConstants.PSNAT_TABLE, routerName);
+        FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.PSNAT_TABLE, pSNatFlowRef);
 
-        LOG.info("NAT Service : Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        LOG.info("NAT Service : Remove the flow in the " + NwConstants.PSNAT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
         mdsalManager.removeFlow(pSNatFlowEntity);
 
         //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table (For the
         // traffic which comes from the VMs of the non NAPT switches)
-        String tsFlowRef = getFlowRefTs(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, routerId);
-        FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef);
+        String tsFlowRef = getFlowRefTs(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
+        FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, tsFlowRef);
 
-        LOG.info("NAT Service : Remove the flow in the " + NatConstants.TERMINATING_SERVICE_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        LOG.info("NAT Service : Remove the flow in the " + NwConstants.INTERNAL_TUNNEL_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
         mdsalManager.removeFlow(tsNatFlowEntity);
 
         //Remove the Outbound flow entry which forwards the packet to FIB Table
-        String outboundNatFlowRef = getFlowRefOutbound(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
-        FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef);
+        String outboundNatFlowRef = getFlowRefOutbound(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
+        FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef);
 
-        LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        LOG.info("NAT Service : Remove the flow in the " + NwConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
         mdsalManager.removeFlow(outboundNatFlowEntity);
 
+        removeNaptFibExternalOutputFlows(routerId, dpnId, networkId, externalIps);
+
         //Remove the NAPT PFIB TABLE which forwards the incoming packet to FIB Table matching on the router ID.
-        String natPfibFlowRef = getFlowRefTs(dpnId, NatConstants.NAPT_PFIB_TABLE, routerId);
-        FlowEntity natPfibFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.NAPT_PFIB_TABLE, natPfibFlowRef);
+        String natPfibFlowRef = getFlowRefTs(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
+        FlowEntity natPfibFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.NAPT_PFIB_TABLE, natPfibFlowRef);
 
-        LOG.info("NAT Service : Remove the flow in the " + NatConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        LOG.info("NAT Service : Remove the flow in the " + NwConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
         mdsalManager.removeFlow(natPfibFlowEntity);
 
         //Long vpnId = NatUtil.getVpnId(dataBroker, routerId); - This does not work since ext-routers is deleted already - no network info
@@ -1583,9 +1627,9 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
         if(vpnId != NatConstants.INVALID_ID){
            //Remove the NAPT PFIB TABLE which forwards the outgoing packet to FIB Table matching on the VPN ID.
-           String natPfibVpnFlowRef = getFlowRefTs(dpnId, NatConstants.NAPT_PFIB_TABLE, vpnId);
-           FlowEntity natPfibVpnFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.NAPT_PFIB_TABLE, natPfibVpnFlowRef);
-           LOG.info("NAT Service : Remove the flow in the " + NatConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and VPN ID {}", dpnId, vpnId);
+           String natPfibVpnFlowRef = getFlowRefTs(dpnId, NwConstants.NAPT_PFIB_TABLE, vpnId);
+           FlowEntity natPfibVpnFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.NAPT_PFIB_TABLE, natPfibVpnFlowRef);
+           LOG.info("NAT Service : Remove the flow in the " + NwConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and VPN ID {}", dpnId, vpnId);
            mdsalManager.removeFlow(natPfibVpnFlowEntity);
         }
 
@@ -1610,10 +1654,10 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                 String internalPort = ipPortParts[1];
 
                 //Build the flow for the outbound NAPT table
-                String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId), internalIp, Integer.valueOf(internalPort));
-                FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+                String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId), internalIp, Integer.valueOf(internalPort));
+                FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
 
-                LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                LOG.info("NAT Service : Remove the flow in the " + NwConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
                 mdsalManager.removeFlow(outboundNaptFlowEntity);
 
                 IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
@@ -1621,15 +1665,49 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                 int externalPort = ipPortExternal.getPortNum();
 
                 //Build the flow for the inbound NAPT table
-                switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
-                FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+                switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
+                FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.INBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
 
-                LOG.info("NAT Service : Remove the flow in the " + NatConstants.INBOUND_NAPT_TABLE + " for the active active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                LOG.info("NAT Service : Remove the flow in the " + NwConstants.INBOUND_NAPT_TABLE + " for the active active switch with the DPN ID {} and router ID {}", dpnId, routerId);
                 mdsalManager.removeFlow(inboundNaptFlowEntity);
             }
         }
     }
 
+    protected void removeNaptFibExternalOutputFlows(long routerId, BigInteger dpnId, Uuid networkId,
+            List<String> externalIps) {
+        Long extVpnId = null;
+        if (networkId != null) {
+            Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
+            if (vpnUuid != null) {
+                extVpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
+            } else {
+                LOG.debug("NAT Service: removeNaptFibExternalOutputFlows - vpnUuid is null");
+            }
+        } else {
+            LOG.debug("NAT Service: removeNaptFibExternalOutputFlows - networkId is null");
+            extVpnId = NatUtil.getVpnId(dataBroker, routerId);
+        }
+        if (extVpnId == null || extVpnId == NatConstants.INVALID_ID) {
+            LOG.debug("removeNaptFibExternalOutputFlows - extVpnId not found for routerId {}", routerId);
+            extVpnId = routerId;
+        }
+        for (String ip : externalIps) {
+            String extIp = removeMaskFromIp(ip);
+            String naptFlowRef = getFlowRefNaptFib(dpnId, NwConstants.NAPT_PFIB_TABLE, extVpnId, extIp);
+            LOG.info("NAT Service: Remove the flow in the " + NwConstants.NAPT_PFIB_TABLE + " for the active switch"
+                    + " with the DPN ID {} and router ID {} and IP {} flowRef {}", dpnId, routerId, extIp, naptFlowRef);
+            mdsalManager.removeFlow(NatUtil.buildFlowEntity(dpnId,NwConstants.NAPT_PFIB_TABLE, naptFlowRef));
+        }
+    }
+
+    private String removeMaskFromIp(String ip) {
+        if (ip != null && !ip.trim().isEmpty()) {
+            return ip.split("/")[0];
+        }
+        return ip;
+    }
+
      public void removeNaptFlowsFromActiveSwitchInternetVpn(long routerId, String routerName, BigInteger dpnId, Uuid networkId, String vpnName){
 
          LOG.debug("NAT Service : Remove NAPT flows from Active switch Internet Vpn");
@@ -1646,9 +1724,9 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
          if(vpnId != NatConstants.INVALID_ID){
             //Remove the NAPT PFIB TABLE which forwards the outgoing packet to FIB Table matching on the VPN ID.
-            String natPfibVpnFlowRef = getFlowRefTs(dpnId, NatConstants.NAPT_PFIB_TABLE, vpnId);
-            FlowEntity natPfibVpnFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.NAPT_PFIB_TABLE, natPfibVpnFlowRef);
-            LOG.info("NAT Service : Remove the flow in the " + NatConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and VPN ID {}", dpnId, vpnId);
+            String natPfibVpnFlowRef = getFlowRefTs(dpnId, NwConstants.NAPT_PFIB_TABLE, vpnId);
+            FlowEntity natPfibVpnFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.NAPT_PFIB_TABLE, natPfibVpnFlowRef);
+            LOG.info("NAT Service : Remove the flow in the " + NwConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and VPN ID {}", dpnId, vpnId);
             mdsalManager.removeFlow(natPfibVpnFlowEntity);
 
              // Remove IP-PORT active NAPT entries and release port from IdManager
@@ -1672,10 +1750,10 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                      String internalPort = ipPortParts[1];
 
                      //Build the flow for the outbound NAPT table
-                     String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId), internalIp, Integer.valueOf(internalPort));
-                     FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+                     String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId), internalIp, Integer.valueOf(internalPort));
+                     FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
 
-                     LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                     LOG.info("NAT Service : Remove the flow in the " + NwConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
                      mdsalManager.removeFlow(outboundNaptFlowEntity);
 
                      IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
@@ -1683,10 +1761,10 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                      int externalPort = ipPortExternal.getPortNum();
 
                      //Build the flow for the inbound NAPT table
-                     switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
-                     FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+                     switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
+                     FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.INBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
 
-                     LOG.info("NAT Service : Remove the flow in the " + NatConstants.INBOUND_NAPT_TABLE + " for the active active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                     LOG.info("NAT Service : Remove the flow in the " + NwConstants.INBOUND_NAPT_TABLE + " for the active active switch with the DPN ID {} and router ID {}", dpnId, routerId);
                      mdsalManager.removeFlow(inboundNaptFlowEntity);
 
                      // Finally release port from idmanager
@@ -1718,10 +1796,10 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                 LOG.info("NAT Service : Handle Ordinary switch");
 
                 //Remove the PSNAT entry which forwards the packet to Terminating Service table
-                String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, String.valueOf(routerName));
-                FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef);
+                String pSNatFlowRef = getFlowRefSnat(dpnId, NwConstants.PSNAT_TABLE, String.valueOf(routerName));
+                FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NwConstants.PSNAT_TABLE, pSNatFlowRef);
 
-                LOG.info("Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the non active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                LOG.info("Remove the flow in the " + NwConstants.PSNAT_TABLE + " for the non active switch with the DPN ID {} and router ID {}", dpnId, routerId);
                 mdsalManager.removeFlow(pSNatFlowEntity);
 
                 //Remove the group entry which forwards the traffic to the out port (VXLAN tunnel).
@@ -1932,7 +2010,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         //Inform BGP about the route removal
         LOG.info("Informing BGP to remove route for externalIP {} of vpn {}",externalIp,vpnName);
         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
-        NatUtil.removePrefixFromBGP(bgpManager, rd, externalIp, LOG);
+        NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp, LOG);
     }
 
     private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
@@ -1967,15 +2045,10 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         LOG.debug("NAT Service : LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
     }
 
-    protected InstanceIdentifier<Routers> getWildCardPath()
-    {
-        return InstanceIdentifier.create(ExtRouters.class).child(Routers.class);
-    }
-
-
     /**
      * router association to vpn
-     *
+     *@param routerName - Name of router
+     *@param bgpVpnName BGP VPN name
      */
     public void changeLocalVpnIdToBgpVpnId(String routerName, String bgpVpnName){
         LOG.debug("NAT Service : Router associated to BGP VPN");
@@ -2006,7 +2079,8 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
     /**
      * router disassociation from vpn
-     *
+     *@param routerName - Name of router
+     *@param bgpVpnName BGP VPN name
      */
     public void changeBgpVpnIdToLocalVpnId(String routerName, String bgpVpnName){
         LOG.debug("NAT Service : Router dissociated from BGP VPN");
@@ -2037,9 +2111,13 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         }
         return false;
     }
-
     public void installFlowsWithUpdatedVpnId(BigInteger primarySwitchId, String routerName, long bgpVpnId, long
             routerId, boolean isSnatCfgd){
+        installFlowsWithUpdatedVpnId(primarySwitchId, routerName, bgpVpnId, routerId, isSnatCfgd, null);
+    }
+
+    public void installFlowsWithUpdatedVpnId(BigInteger primarySwitchId, String routerName, long bgpVpnId, long
+            routerId, boolean isSnatCfgd, Routers router){
         long changedVpnId = bgpVpnId;
         String logMsg = "NAT Service : Update the BGP VPN ID {}";
         if (bgpVpnId == NatConstants.INVALID_ID){
@@ -2135,9 +2213,9 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                 SessionAddress internalAddress = new SessionAddress(internalIp, Integer.valueOf(internalPort));
                 SessionAddress externalAddress = naptManager.getExternalAddressMapping(routerId, internalAddress, protocol);
                 long internetVpnid = NatUtil.getVpnId(dataBroker, routerId);
-                naptEventHandler.buildAndInstallNatFlows(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, internetVpnid, routerId, bgpVpnId,
+                NaptEventHandler.buildAndInstallNatFlows(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, internetVpnid, routerId, bgpVpnId,
                         internalAddress, externalAddress, protocol);
-                naptEventHandler.buildAndInstallNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, internetVpnid, routerId, bgpVpnId,
+                NaptEventHandler.buildAndInstallNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, internetVpnid, routerId, bgpVpnId,
                         externalAddress, internalAddress, protocol);
 
             }
@@ -2151,7 +2229,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[] { 0x0800L }));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                BigInteger.valueOf(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+                MetaDataUtil.getVpnIdMetadata(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
 
         List<InstructionInfo> instructions = new ArrayList<>();
         List<ActionInfo> actionsInfo = new ArrayList<>();
@@ -2161,11 +2239,11 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         actionsInfo.add(actionSetField);
         LOG.debug("NAT Service : Setting the tunnel to the list of action infos {}", actionsInfo);
         actionsInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
-        instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfo));
-        String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfo));
+        String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
 
         LOG.debug("NAT Service : Returning SNAT Flow Entity {}", flowEntity);
         return flowEntity;
@@ -2178,16 +2256,16 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[] { 0x0800L }));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                BigInteger.valueOf(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+                MetaDataUtil.getVpnIdMetadata(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
 
         List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]
-                { NatConstants.OUTBOUND_NAPT_TABLE }));
+                { NwConstants.OUTBOUND_NAPT_TABLE }));
 
-        String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+        String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
 
         LOG.debug("NAT Service : Returning SNAT Flow Entity {}", flowEntity);
         return flowEntity;
@@ -2212,13 +2290,13 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]
-                { bgpVpnIdAsBigInt, MetaDataUtil.METADATA_MASK_VRFID }));
+                { MetaDataUtil.getVpnIdMetadata(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]
-                { NatConstants.OUTBOUND_NAPT_TABLE }));
-        String flowRef = getFlowRefTs(dpId, NatConstants.TERMINATING_SERVICE_TABLE, routerId.longValue());
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.TERMINATING_SERVICE_TABLE, flowRef,
+                { NwConstants.OUTBOUND_NAPT_TABLE }));
+        String flowRef = getFlowRefTs(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue());
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
                 NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_TS_TABLE, matches, instructions);
+                NwConstants.COOKIE_TS_TABLE, matches, instructions);
         return flowEntity;
     }
 
@@ -2235,23 +2313,47 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[]{0x0800L}));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[]{
-                BigInteger.valueOf(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID}));
+                MetaDataUtil.getVpnIdMetadata(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID}));
 
         List<InstructionInfo> instructions = new ArrayList<>();
         List<ActionInfo> actionsInfos = new ArrayList<>();
         actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, new String[] {}));
         instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
-        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID}));
+        instructions.add(new InstructionInfo(InstructionType.write_metadata,
+                new BigInteger[] { MetaDataUtil.getVpnIdMetadata(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
 
-        String flowRef = getFlowRefOutbound(dpId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
+        String flowRef = getFlowRefOutbound(dpId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
         BigInteger cookie = getCookieOutboundFlow(routerId);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.OUTBOUND_NAPT_TABLE, flowRef,
-                5, flowRef, 0, 0,
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef, 5, flowRef, 0, 0,
                 cookie, matches, instructions);
         LOG.debug("NAT Service : returning flowEntity {}", flowEntity);
         return flowEntity;
     }
 
+    protected FlowEntity buildNaptFibExternalOutputFlowEntity(BigInteger dpId, long extVpnId, Uuid subnetId, String externalIp) {
+        LOG.debug("NAT Service : buildNaptFibExternalOutputFlowEntity called for dpId {}, routerId {}, srcIp {}", dpId, extVpnId,
+                externalIp);
+
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type, new long[] { NwConstants.ETHTYPE_IPV4 }));
+        matches.add(new MatchInfo(MatchFieldType.metadata,
+                new BigInteger[] { MetaDataUtil.getVpnIdMetadata(extVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+        matches.add(new MatchInfo(MatchFieldType.ipv4_source, new String[] { externalIp , "32" }));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        List<ActionInfo> actionsInfos = new ArrayList<>();
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+        long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(subnetId.getValue()), idManager);
+        actionsInfos.add(new ActionInfo(ActionType.group, new String[] { String.valueOf(groupId) }));
+
+        String flowRef = getFlowRefNaptFib(dpId, NwConstants.NAPT_PFIB_TABLE, extVpnId, externalIp);
+        BigInteger cookie = getCookieOutboundFlow(extVpnId);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.NAPT_PFIB_TABLE, flowRef, 6, flowRef, 0,
+                0, cookie, matches, instructions);
+        LOG.debug("NAT Service : returning flowEntity {}", flowEntity);
+        return flowEntity;
+    }
+
     public void installNaptPfibEntryWithBgpVpn(BigInteger dpnId, long segmentId, long changedVpnId) {
         LOG.debug("NAT Service : installNaptPfibEntryWithBgpVpn called for dpnId {} and segmentId {} ,BGP VPN ID {}", dpnId, segmentId, changedVpnId);
         FlowEntity naptPfibFlowEntity = buildNaptPfibFlowEntityWithUpdatedVpnId(dpnId, segmentId, changedVpnId);
@@ -2265,17 +2367,18 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         matches.add(new MatchInfo(MatchFieldType.eth_type,
                 new long[] { 0x0800L }));
         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                BigInteger.valueOf(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+                MetaDataUtil.getVpnIdMetadata(changedVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
 
         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
-        listActionInfo.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) }));
+        listActionInfo.add(new ActionInfo(ActionType.nx_load_in_port, new BigInteger[]{BigInteger.ZERO}));
+        listActionInfo.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NwConstants.L3_FIB_TABLE) }));
         instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
 
-        String flowRef = getFlowRefTs(dpId, NatConstants.NAPT_PFIB_TABLE, segmentId);
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.NAPT_PFIB_TABLE, flowRef,
+        String flowRef = getFlowRefTs(dpId, NwConstants.NAPT_PFIB_TABLE, segmentId);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.NAPT_PFIB_TABLE, flowRef,
                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
-                NatConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
 
         LOG.debug("NAT Service : Returning NaptPFib Flow Entity {}", flowEntity);
         return flowEntity;