Bump versions by 0.1.0 for next dev cycle
[vpnservice.git] / natservice / natservice-impl / src / main / java / org / opendaylight / vpnservice / natservice / internal / ExternalRoutersListener.java
index 3e6db2d01b287ff667ad726d051df8d346f12f7a..87b9ee60e165674dd33d0f73854967ba46e62a92 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright (c) 2015 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,
@@ -9,14 +9,22 @@
 package org.opendaylight.vpnservice.natservice.internal;
 
 import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 
 import org.opendaylight.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.vpnservice.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ProtocolTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeGre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeVxlan;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.*;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
@@ -70,6 +78,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev1512
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExtRouters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.NaptSwitches;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.RouterIdName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.RoutersKey;
@@ -84,7 +93,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev15
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.Subnetmap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -93,6 +101,8 @@ 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;
@@ -121,6 +131,16 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
     private SNATDefaultRouteProgrammer defaultRouteProgrammer;
     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) {
         this.mdsalManager = mdsalManager;
@@ -173,7 +193,7 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
     @Override
     protected void add(InstanceIdentifier<Routers> identifier, Routers routers) {
 
-        LOG.info( "Add external router event for {}", routers.getRouterName() );
+        LOG.info( "NAT Service : Add external router event for {}", routers.getRouterName() );
 
         LOG.info("Installing NAT default route on all dpns part of router {}", routers.getRouterName());
         addOrDelDefFibRouteToSNAT(routers.getRouterName(), true);
@@ -189,33 +209,81 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         RouterIds rtrs = new RouterIdsBuilder().setKey(new RouterIdsKey(routerId)).setRouterId(routerId).setRouterName(routerName).build();
         MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.CONFIGURATION, getRoutersIdentifier(routerId), rtrs);
 
-        handleEnableSnat(routerName);
+        handleEnableSnat(routers);
     }
 
-    public void handleEnableSnat(String routerName){
-        LOG.info("Handling SNAT for router {}", routerName);
+    public void handleEnableSnat(Routers routers){
+        String routerName = routers.getRouterName();
+        LOG.info("NAT Service : Handling SNAT for router {}", routerName);
+
+        long segmentId = NatUtil.getVpnId(dataBroker, routerName);
+        naptManager.initialiseExternalCounter(routers, segmentId);
 
         // Allocate Primary Napt Switch for this router
         BigInteger 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");
+        }
         LOG.debug("NAT Service : About to create and install outbound miss entry in Primary Switch {} for router {}", primarySwitchId, routerName);
-        // write metadata and punt
-        installOutboundMissEntry(routerName, primarySwitchId);
-        // Now install entries in SNAT tables to point to Primary for each router
-        List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
-        for(BigInteger dpnId : switches) {
-              // Handle switches and NAPT switches separately
-              if( dpnId != primarySwitchId ) {
-                   LOG.debug("NAT Service : Handle Ordinary switch");
-                   handleSwitches(dpnId, routerName, primarySwitchId);
-              } else {
-                   LOG.debug("NAT Service : Handle NAPT switch");
-                   handlePrimaryNaptSwitch(dpnId, routerName, primarySwitchId);
-              }
+
+        long bgpVpnId = NatConstants.INVALID_ID;
+        Uuid bgpVpnUuid = NatUtil.getVpnForRouter(dataBroker, routerName);
+        if(bgpVpnUuid != null){
+            bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
+        }
+        if(bgpVpnId != NatConstants.INVALID_ID){
+
+            String bgpVpnName = bgpVpnUuid.getValue();
+            LOG.debug("Populate the router-id-name container with the mapping BGP VPN-ID {} -> BGP VPN-NAME {}", bgpVpnId, bgpVpnName);
+            RouterIds rtrs = new RouterIdsBuilder().setKey(new RouterIdsKey(bgpVpnId)).setRouterId(bgpVpnId).setRouterName(bgpVpnName).build();
+            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getRoutersIdentifier(bgpVpnId), rtrs);
+
+            long groupId = 0;
+            long routerId = NatUtil.getVpnId(dataBroker, routerName);
+            List<BigInteger> switches = NatUtil.getDpnsForRouter(dataBroker, routerName);
+            if(switches == null){
+                LOG.error("NAT Service : No DPNS associated for the router {}", routerName);
+                return;
+            }
+            for (BigInteger dpnId : switches) {
+                // Handle switches and NAPT switches separately
+                if (!dpnId.equals(primarySwitchId)) {
+                    LOG.debug("NAT Service : Install group in Ordinary switch {}", dpnId);
+                    List<BucketInfo> bucketInfoForNonNaptSwitches = getBucketInfoForNonNaptSwitches(dpnId, primarySwitchId, routerName);
+                    groupId = installGroup(dpnId, routerName, bucketInfoForNonNaptSwitches);
+                }else{
+                    LOG.debug("NAT Service : Install group in Primary switch {}", dpnId);
+                    List<BucketInfo> bucketInfoForNaptSwitches = getBucketInfoForPrimaryNaptSwitch();
+                    groupId = installGroup(dpnId, routerName, bucketInfoForNaptSwitches);
+
+                    Long vpnId = NatUtil.getVpnId(dataBroker, routerId);
+                    //Install the NAPT PFIB TABLE which forwards the outgoing packet to FIB Table matching on the VPN ID.
+                    if(vpnId != null && vpnId != NatConstants.INVALID_ID) {
+                        installNaptPfibEntry(dpnId, vpnId);
+                    }
+
+                }
+                installFlowsWithUpdatedVpnId(primarySwitchId, routerName, groupId, bgpVpnId, routerId);
+            }
+        }else {
+            // write metadata and punt
+            installOutboundMissEntry(routerName, primarySwitchId);
+            // Now install entries in SNAT tables to point to Primary for each router
+            List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
+            for (BigInteger dpnId : switches) {
+                // Handle switches and NAPT switches separately
+                if (!dpnId.equals(primarySwitchId)) {
+                    LOG.debug("NAT Service : Handle Ordinary switch");
+                    handleSwitches(dpnId, routerName, primarySwitchId);
+                } else {
+                    LOG.debug("NAT Service : Handle NAPT switch");
+                    handlePrimaryNaptSwitch(dpnId, routerName, primarySwitchId);
+                }
+            }
         }
 
         // call registerMapping Api
-        long segmentId = NatUtil.getVpnId(dataBroker, routerName);
         LOG.debug("NAT Service : Preparing to call registerMapping for routerName {} and Id {}", routerName, segmentId);
 
         List<Uuid> subnetList = null;
@@ -310,28 +378,86 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
     }
 
     private void addOrDelDefFibRouteToSNAT(String routerName, boolean create) {
+        //Check if BGP VPN exists. If exists then invoke the new method.
+        long bgpVpnId = NatUtil.getBgpVpnId(dataBroker, routerName);
+        if(bgpVpnId != NatConstants.INVALID_ID) {
+            addOrDelDefaultFibRouteForSNATWIthBgpVpn(routerName, bgpVpnId, create);
+            return;
+        }
+
         //Router ID is used as the internal VPN's name, hence the vrf-id in VpnInstance Op DataStore
-        InstanceIdentifier<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerName);
+        addOrDelDefaultFibRouteForSNAT(routerName, create);
+/*        InstanceIdentifier<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerName);
         Optional<VpnInstanceOpDataEntry> vpnInstOp = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
         if (vpnInstOp.isPresent()) {
-            List<VpnToDpnList> dpnListInVpn = vpnInstOp.get().getVpnToDpnList();
-            if(dpnListInVpn == null) {
-                LOG.debug("Current no dpns part of router {} to program default NAT route", routerName);
-                return;
+            addOrDelDefaultFibRouteForSNAT(routerName, create);
+        } *//*else {
+            //Check if this router is associated with any external VPN
+            LOG.debug("Checking if router {} is associated with BGP VPN", routerName);
+            Uuid vpnId = NatUtil.getVpnForRouter(dataBroker, routerName);
+            if(vpnId != null) {
+                String vpnName = vpnId.getValue();
+                LOG.debug("Router {} is associated with VPN {}", routerName, vpnName);
+                InstanceIdentifier<VpnInstanceOpDataEntry> vid = NatUtil.getVpnInstanceOpDataIdentifier(vpnName);
+                vpnInstOp = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, vid);
+                if (vpnInstOp.isPresent()) {
+                    addOrDelDefaultFibRouteForSNAT(routerName, vpnInstOp.get(), create);
+                }
             }
-            long vpnId = NatUtil.readVpnId(dataBroker, routerName);
-            if(vpnId == NatConstants.INVALID_ID) {
-                LOG.error("Could not retrieve router Id for {} to program default NAT route in FIB", routerName);
+        }*/
+    }
+
+    private void addOrDelDefaultFibRouteForSNAT(String routerName, boolean create) {
+/*
+        List<VpnToDpnList> dpnListInVpn = vpnInstOp.getVpnToDpnList();
+        List<BigInteger> switches = new ArrayList<>();
+        if(dpnListInVpn == null || dpnListInVpn.isEmpty()) {
+            LOG.debug("NAT Service : Unable to get the switches for the router {} from the VPNInstanceOpData", routerName);
+            switches = NatUtil.getDpnsForRouter(dataBroker, routerName);
+            if(switches == null || switches.isEmpty()){
+                LOG.error("NAT Service : addOrDelDefaultFibRouteForSNAT : NO SWITCHES ARE PART OF ROUTER {}", routerName);
                 return;
             }
+        }else{
             for (VpnToDpnList dpn : dpnListInVpn) {
-                BigInteger dpnId = dpn.getDpnId();
-                if (create == true) {
-                    //installDefNATRouteInDPN(dpnId, vpnId);
-                    defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
-                } else {
-                    //removeDefNATRouteInDPN(dpnId, vpnId);
-                    defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
+                switches.add(dpn.getDpnId());
+            }
+        }
+*/
+        List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
+        long routerId = NatUtil.readVpnId(dataBroker, routerName);
+        if(routerId == NatConstants.INVALID_ID) {
+            LOG.error("Could not retrieve router Id for {} to program default NAT route in FIB", routerName);
+            return;
+        }
+        for (BigInteger dpnId : switches) {
+            if (create == true) {
+                defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, routerId);
+            } else {
+                defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, routerId);
+            }
+        }
+    }
+
+    private void addOrDelDefaultFibRouteForSNATWIthBgpVpn(String routerName, long bgpVpnId, boolean create) {
+        List<BigInteger> dpnIds = NatUtil.getDpnsForRouter(dataBroker, routerName);
+        if(dpnIds == null || dpnIds.isEmpty()) {
+            LOG.debug("NAT Service : Current no dpns part of router {} to program default NAT route", routerName);
+            return;
+        }
+        long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        for (BigInteger dpnId : dpnIds) {
+            if (create == true) {
+                if(bgpVpnId != NatConstants.INVALID_ID) {
+                    defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, bgpVpnId, routerId);
+                }else{
+                    defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, routerId);
+                }
+            } else {
+                if(bgpVpnId != NatConstants.INVALID_ID) {
+                    defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, bgpVpnId, routerId);
+                }else{
+                    defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, routerId);
                 }
             }
         }
@@ -424,12 +550,28 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
     }
 
     protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
+        Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
+        RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
         try {
             Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
                                                                                  .setSourceDpid(srcDpId)
-                                                                                 .setDestinationDpid(dstDpId).build());
-            RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
+                                                                                 .setDestinationDpid(dstDpId)
+//                                                                                .setTunnelType(tunType)
+                                                                                .build());
+            rpcResult = result.get();
             if(!rpcResult.isSuccessful()) {
+                tunType = TunnelTypeGre.class ;
+                result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
+                        .setSourceDpid(srcDpId)
+                        .setDestinationDpid(dstDpId)
+//                        .setTunnelType(tunType)
+                        .build());
+                rpcResult = result.get();
+                if(!rpcResult.isSuccessful()) {
+                    LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
+                } else {
+                    return rpcResult.getResult().getInterfaceName();
+                }
                 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
             } else {
                 return rpcResult.getResult().getInterfaceName();
@@ -489,6 +631,14 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         mdsalManager.installFlow(flowEntity);
     }
 
+    long installGroup(BigInteger dpnId, String routerName, List<BucketInfo> bucketInfo){
+        long groupId = createGroupId(getGroupIdKey(routerName));
+        GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, bucketInfo);
+        LOG.debug("NAT Service : installing the SNAT to NAPT GroupEntity:{}", groupEntity);
+        mdsalManager.installGroup(groupEntity);
+        return groupId;
+    }
+
     public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId) {
 
         LOG.debug("NAT Service : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}", dpId, routerName, groupId );
@@ -610,7 +760,21 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         installSnatMissEntry(dpnId, listBucketInfo, routerName);
 
     }
+    List<BucketInfo> getBucketInfoForNonNaptSwitches(BigInteger nonNaptSwitchId, BigInteger primarySwitchId, String routerName) {
+        List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
+        String ifNamePrimary = getTunnelInterfaceName(nonNaptSwitchId, primarySwitchId);
+        List<BucketInfo> listBucketInfo = new ArrayList<>();
+        long routerId = NatUtil.getVpnId(dataBroker, routerName);
+
+        if (ifNamePrimary != null) {
+            LOG.debug("NAT Service : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
+            listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
+        }
+        BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
 
+        listBucketInfo.add(0, bucketPrimary);
+        return listBucketInfo;
+    }
     protected void handlePrimaryNaptSwitch (BigInteger dpnId, String routerName, BigInteger primarySwitchId) {
 
            /*
@@ -629,31 +793,45 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
             installSnatMissEntry(dpnId, listBucketInfo, routerName);
             installTerminatingServiceTblEntry(dpnId, routerName);
+            //Install the NAPT PFIB TABLE which forwards the outgoing packet to FIB Table matching on the router ID.
             installNaptPfibEntry(dpnId, routerId);
+            Long vpnId = NatUtil.getVpnId(dataBroker, routerId);
+           //Install the NAPT PFIB TABLE which forwards the outgoing packet to FIB Table matching on the VPN ID.
+            if(vpnId != null && vpnId != NatConstants.INVALID_ID) {
+                installNaptPfibEntry(dpnId, vpnId);
+            }
+    }
 
+    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)}));
+        BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+        listBucketInfo.add(0, bucketPrimary);
+        return listBucketInfo;
     }
 
-    public void installNaptPfibEntry(BigInteger dpnId, long routerId) {
-        LOG.debug("NAT Service : installNaptPfibEntry called for dpnId {} and routerId {} ", dpnId, routerId);
-        FlowEntity flowEntity = buildNaptPfibFlowEntity(dpnId, routerId);
-        mdsalManager.installFlow(flowEntity);
+    public void installNaptPfibEntry(BigInteger dpnId, long segmentId) {
+        LOG.debug("NAT Service : installNaptPfibEntry called for dpnId {} and segmentId {} ", dpnId, segmentId);
+        FlowEntity naptPfibFlowEntity = buildNaptPfibFlowEntity(dpnId, segmentId);
+        mdsalManager.installFlow(naptPfibFlowEntity);
     }
 
-    public FlowEntity buildNaptPfibFlowEntity(BigInteger dpId, long routerId) {
+    public FlowEntity buildNaptPfibFlowEntity(BigInteger dpId, long segmentId) {
 
-        LOG.debug("NAT Service : buildNaptPfibFlowEntity is called for dpId {}, routerId {}", dpId, routerId );
+        LOG.debug("NAT Service : buildNaptPfibFlowEntity is called for dpId {}, segmentId {}", dpId, segmentId );
         List<MatchInfo> matches = new ArrayList<MatchInfo>();
         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 }));
+                BigInteger.valueOf(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) }));
         instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
 
-        String flowRef = getFlowRefTs(dpId, NatConstants.NAPT_PFIB_TABLE, routerId);
+        String flowRef = getFlowRefTs(dpId, NatConstants.NAPT_PFIB_TABLE, segmentId);
         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.NAPT_PFIB_TABLE, flowRef,
                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
                 NatConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
@@ -703,24 +881,29 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
                     NatUtil.addPrefixToBGP(bgpManager, rd, externalIp, nextHopIp, label, log);
 
                     //Get IPMaps from the DB for the router ID
-                    List<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
-
-                    for (IpMap dbIpMap : dbIpMaps) {
-                        String dbExternalIp = dbIpMap.getExternalIp();
-                        //Select the IPMap, whose external IP is the IP for which FIB is installed
-                        if (externalIp.contains(dbExternalIp)) {
-                            String dbInternalIp = dbIpMap.getInternalIp();
-                            IpMapKey dbIpMapKey = dbIpMap.getKey();
-                            IpMap newIpm = new IpMapBuilder().setKey(dbIpMapKey).setInternalIp(dbInternalIp).setExternalIp(dbExternalIp).setLabel(label).build();
-                            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, naptManager.getIpMapIdentifier(routerId, dbInternalIp), newIpm);
+                    List<IpMap> dbIpMaps = NaptManager.getIpMapList(dataBroker, routerId);
+                    if (dbIpMaps != null) {
+                        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)) {
+                                String dbInternalIp = dbIpMap.getInternalIp();
+                                IpMapKey dbIpMapKey = dbIpMap.getKey();
+                                LOG.debug("Setting label {} for internalIp {} and externalIp {}", label, dbInternalIp, externalIp);
+                                IpMap newIpm = new IpMapBuilder().setKey(dbIpMapKey).setInternalIp(dbInternalIp).setExternalIp(dbExternalIp).setLabel(label).build();
+                                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, naptManager.getIpMapIdentifier(routerId, dbInternalIp), newIpm);
+                                break;
+                            }
                         }
+                    } else {
+                        LOG.error("NAT Service : Failed to write label {} for externalIp {} for routerId {} in DS", label, externalIp, routerId);
                     }
 
                     //Install custom FIB routes
                     List<Instruction> customInstructions = new ArrayList<>();
                     customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(0));
                     makeTunnelTableEntry(dpnId, label, customInstructions);
-                    makeLFibTableEntry(dpnId, label, customInstructions);
+                    makeLFibTableEntry(dpnId, label, tableId);
 
                     CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId)
                             .setIpAddress(externalIp).setServiceId(label).setInstruction(customInstructions).build();
@@ -752,18 +935,18 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
             });
      }
 
-    private void makeLFibTableEntry(BigInteger dpId, long serviceId, List<Instruction> customInstructions) {
+    private void makeLFibTableEntry(BigInteger dpId, long serviceId, long tableId) {
         List<MatchInfo> matches = new ArrayList<MatchInfo>();
         matches.add(new MatchInfo(MatchFieldType.eth_type,
-                new long[] { 0x8847L }));
+                new long[]{0x8847L}));
         matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
 
         List<Instruction> instructions = new ArrayList<Instruction>();
         List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
         actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
-        Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
+        Instruction writeInstruction = new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0);
         instructions.add(writeInstruction);
-        instructions.addAll(customInstructions);
+        instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(1));
 
         // Install the flow entry in L3_LFIB_TABLE
         String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
@@ -785,8 +968,8 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
 
         Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
-                getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
-                0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
+                getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d", "TST Flow Entry ", serviceId),
+                0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, customInstructions);
 
         mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
     }
@@ -805,22 +988,292 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
 
     @Override
     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);
+        Uuid networkId = original.getNetworkId();
+
+        // Check if its update on SNAT flag
         boolean originalSNATEnabled = original.isEnableSnat();
         boolean updatedSNATEnabled = update.isEnableSnat();
+        LOG.debug("NAT Service : update of externalRoutersListener called with originalFlag and updatedFlag as {} and {}", originalSNATEnabled, updatedSNATEnabled);
         if(originalSNATEnabled != updatedSNATEnabled) {
             if(originalSNATEnabled) {
                 //SNAT disabled for the router
-                String routerName = original.getRouterName();
                 Uuid networkUuid = original.getNetworkId();
-                List<String> externalIps = original.getExternalIps();
                 LOG.info("NAT Service : SNAT disabled for Router {}", routerName);
-                handleDisableSnat(routerName, networkUuid, externalIps);
+                if (routerId == NatConstants.INVALID_ID) {
+                    LOG.error("NAT Service : Invalid routerId returned for routerName {}", routerName);
+                    return;
+                }
+                List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker,routerId);
+                handleDisableSnat(routerName, networkUuid, externalIps, false, null);
             } else {
-                String routerName = original.getRouterName();
                 LOG.info("NAT Service : SNAT enabled for Router {}", original.getRouterName());
-                handleEnableSnat(routerName);
+                handleEnableSnat(original);
+            }
+        }
+
+        //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();
+        List<String> updatedExternalIpsList = update.getExternalIps();
+        Set<String> originalExternalIps = Sets.newHashSet(originalExternalIpsList);
+        Set<String> updatedExternalIps = Sets.newHashSet(updatedExternalIpsList);
+
+        //Check if the External IPs are added during the update.
+        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");
+            for (String addedExternalIp : addedExternalIps) {
+                /*
+                    1) Do nothing in the IntExtIp model.
+                    2) Initialise the count of the added external IP to 0 in the ExternalCounter model.
+                */
+                String[] externalIpParts = NatUtil.getExternalIpAndPrefix(addedExternalIp);
+                String externalIp = externalIpParts[0];
+                String externalIpPrefix = externalIpParts[1];
+                String externalpStr = externalIp + "/" + externalIpPrefix;
+                LOG.debug("NAT Service : Initialise the count mapping of the external IP {} for the router ID {} in the ExternalIpsCounter model.",
+                        externalpStr, routerId);
+                naptManager.initialiseNewExternalIpCounter(routerId, externalpStr);
+            }
+            LOG.debug("NAT Service : End processing of the External IPs addition during the update operation");
+        }
+
+        //Check if the External IPs are removed during the update.
+        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");
+            List<String> removedExternalIpsAsList = new ArrayList<>();
+            for (String removedExternalIp : removedExternalIps) {
+             /*
+                1) Remove the mappings in the IntExt IP model which has external IP.
+                2) Remove the external IP in the ExternalCounter model.
+                3) For the corresponding subnet IDs whose external IP mapping was removed, allocate one of the least loaded external IP.
+                   Store the subnet IP and the reallocated external IP mapping in the IntExtIp model.
+                4) Increase the count of the allocated external IP by one.
+                5) Advertise to the BGP if external IP is allocated for the first time for the router i.e. the route for the external IP is absent.
+                6) Remove the NAPT translation entries from Inbound and Outbound NAPT tables for the removed external IPs and also from the model.
+                7) Advertise to the BGP for removing the route for the removed external IPs.
+              */
+
+                String[] externalIpParts = NatUtil.getExternalIpAndPrefix(removedExternalIp);
+                String externalIp = externalIpParts[0];
+                String externalIpPrefix = externalIpParts[1];
+                String externalIpAddrStr = externalIp + "/" + externalIpPrefix;
+
+                LOG.debug("NAT Service : Clear the routes from the BGP and remove the FIB and TS entries for removed external IP {}", externalIpAddrStr);
+                Uuid vpnUuId = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
+                String vpnName = "";
+                if(vpnUuId != null){
+                    vpnName = vpnUuId.getValue();
+                }
+                clrRtsFromBgpAndDelFibTs(dpnId, routerId, externalIpAddrStr, vpnName);
+
+                LOG.debug("NAT Service : Remove the mappings in the IntExtIP model which has external IP.");
+                //Get the internal IPs which are associated to the removed external IPs
+                List<IpMap> ipMaps = naptManager.getIpMapList(dataBroker, routerId);
+                List<String> removedInternalIps = new ArrayList<>();
+                for(IpMap ipMap : ipMaps){
+                    if(ipMap.getExternalIp().equals(externalIpAddrStr)){
+                        removedInternalIps.add(ipMap.getInternalIp());
+                    }
+                }
+
+                LOG.debug("Remove the mappings of the internal IPs from the IntExtIP model.");
+                for(String removedInternalIp : removedInternalIps){
+                    LOG.debug("NAT Service : Remove the IP mapping of the internal IP {} for the router ID {} from the IntExtIP model",
+                            removedInternalIp, routerId);
+                    naptManager.removeFromIpMapDS(routerId, removedInternalIp);
+                }
+
+                LOG.debug("NAT Service : Remove the count mapping of the external IP {} for the router ID {} from the ExternalIpsCounter model.",
+                        externalIpAddrStr, routerId );
+                naptManager.removeExternalIpCounter(routerId, externalIpAddrStr);
+                removedExternalIpsAsList.add(externalIpAddrStr);
+
+                LOG.debug("NAT Service : Allocate the least loaded external IPs to the subnets whose external IPs were removed.");
+                for(String removedInternalIp : removedInternalIps) {
+                    allocateExternalIp(dpnId, routerId, networkId, removedInternalIp);
+                }
+
+                LOG.debug("NAT Service : Remove the NAPT translation entries from Inbound and Outbound NAPT tables for the removed external IPs.");
+                //Get the internalIP and internal Port which were associated to the removed external IP.
+                List<Integer> externalPorts = new ArrayList<>();
+                Map<ProtocolTypes, List<String>> protoTypesIntIpPortsMap = new HashMap<>();
+                InstanceIdentifier<IpPortMapping> ipPortMappingId = InstanceIdentifier.builder(IntextIpPortMap.class)
+                        .child(IpPortMapping.class, new IpPortMappingKey(routerId)).build();
+                Optional<IpPortMapping> ipPortMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, ipPortMappingId);
+                if (ipPortMapping.isPresent()) {
+                    List<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.get().getIntextIpProtocolType();
+                    for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){
+                        ProtocolTypes protoType = intextIpProtocolType.getProtocol();
+                        List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
+                        for(IpPortMap ipPortMap : ipPortMaps){
+                            IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
+                            if(ipPortExternal.getIpAddress().equals(externalIp)){
+                                externalPorts.add(ipPortExternal.getPortNum());
+                                List<String> removedInternalIpPorts = protoTypesIntIpPortsMap.get(protoType);
+                                if(removedInternalIpPorts != null){
+                                    removedInternalIpPorts.add(ipPortMap.getIpPortInternal());
+                                    protoTypesIntIpPortsMap.put(protoType, removedInternalIpPorts);
+                                }else{
+                                    removedInternalIpPorts = new ArrayList<>();
+                                    removedInternalIpPorts.add(ipPortMap.getIpPortInternal());
+                                    protoTypesIntIpPortsMap.put(protoType, removedInternalIpPorts);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                //Remove the IP port map from the intext-ip-port-map model, which were containing the removed external IP.
+                Set<Map.Entry<ProtocolTypes, List<String>>> protoTypesIntIpPorts = protoTypesIntIpPortsMap.entrySet();
+                Map<String, List<String>> internalIpPortMap = new HashMap<>();
+                for(Map.Entry protoTypesIntIpPort : protoTypesIntIpPorts){
+                    ProtocolTypes protocolType = (ProtocolTypes)protoTypesIntIpPort.getKey();
+                    List<String> removedInternalIpPorts = (List<String>)protoTypesIntIpPort.getValue();
+                    for(String removedInternalIpPort : removedInternalIpPorts){
+                        //Remove the IP port map from the intext-ip-port-map model, which were containing the removed external IP
+                        naptManager.removeFromIpPortMapDS(routerId, removedInternalIpPort, protocolType);
+                        //Remove the IP port incomint packer map.
+                        naptPacketInHandler.removeIncomingPacketMap(removedInternalIpPort);
+                        String[] removedInternalIpPortParts = removedInternalIpPort.split(":");
+                        if(removedInternalIpPortParts.length == 2){
+                            String removedInternalIp = removedInternalIpPortParts[0];
+                            String removedInternalPort = removedInternalIpPortParts[1];
+                            List<String> removedInternalPortsList =  internalIpPortMap.get(removedInternalPort);
+                            if (removedInternalPortsList != null){
+                                removedInternalPortsList.add(removedInternalPort);
+                                internalIpPortMap.put(removedInternalIp, removedInternalPortsList);
+                            }else{
+                                removedInternalPortsList = new ArrayList<>();
+                                removedInternalPortsList.add(removedInternalPort);
+                                internalIpPortMap.put(removedInternalIp, removedInternalPortsList);
+                            }
+                        }
+                    }
+                }
+
+                // Delete the entry from SnatIntIpPortMap DS
+                Set<String> internalIps = internalIpPortMap.keySet();
+                for(String internalIp : internalIps){
+                    LOG.debug("NAT Service : Removing IpPort having the internal IP {} from the model SnatIntIpPortMap", internalIp);
+                    naptManager.removeFromSnatIpPortDS(routerId, internalIp);
+                }
+
+                naptManager.removeNaptPortPool(externalIp);
+
+                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);
+                }
+
+                Set<Map.Entry<String, List<String>>> internalIpPorts = internalIpPortMap.entrySet();
+                for(Map.Entry<String, List<String>> internalIpPort : internalIpPorts) {
+                    String internalIp = internalIpPort.getKey();
+                    LOG.debug("Remove the NAPT translation entries from Outbound NAPT tables for the removed internal IP {}", internalIp);
+                    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));
+                    }
+                }
+            }
+            LOG.debug("NAT Service : End processing of the External IPs removal during the update operation");
+        }
+
+        //Check if its Update on subnets
+        LOG.debug("NAT Service : Checking if this is update on subnets");
+        List<Uuid> originalSubnetIdsList = original.getSubnetIds();
+        List<Uuid> updatedSubnetIdsList = update.getSubnetIds();
+        Set<Uuid> originalSubnetIds = Sets.newHashSet(originalSubnetIdsList);
+        Set<Uuid> updatedSubnetIds = Sets.newHashSet(updatedSubnetIdsList);
+        SetView<Uuid> addedSubnetIds = Sets.difference(updatedSubnetIds, originalSubnetIds);
+
+        //Check if the Subnet IDs are added during the update.
+        if(addedSubnetIds.size() != 0){
+            LOG.debug("NAT Service : Start processing of the Subnet IDs addition during the update operation");
+            for(Uuid addedSubnetId : addedSubnetIds){
+                /*
+                1) Select the least loaded external IP for the subnet and store the mapping of the subnet IP and the external IP in the IntExtIp model.
+                2) Increase the count of the selected external IP by one.
+                3) Advertise to the BGP if external IP is allocated for the first time for the router i.e. the route for the external IP is absent.
+                */
+                String subnetIp = NatUtil.getSubnetIp(dataBroker, addedSubnetId);
+                if(subnetIp != null) {
+                    allocateExternalIp(dpnId, routerId, networkId, subnetIp);
+                }
             }
+            LOG.debug("NAT Service : End processing of the Subnet IDs addition during the update operation");
         }
+
+        //Check if the Subnet IDs are removed during the update.
+        SetView<Uuid> removedSubnetIds = Sets.difference(originalSubnetIds, updatedSubnetIds);
+        if(removedSubnetIds.size() != 0){
+            LOG.debug("NAT Service : Start processing of the Subnet IDs removal during the update operation");
+            for(Uuid removedSubnetId : removedSubnetIds){
+                String[] subnetAddr = NatUtil.getSubnetIpAndPrefix(dataBroker, removedSubnetId);
+                if(subnetAddr != null){
+                    /*
+                    1) Remove the subnet IP and the external IP in the IntExtIp map
+                    2) Decrease the count of the coresponding external IP by one.
+                    3) Advertise to the BGP for removing the routes of the corresponding external IP if its not allocated to any other internal IP.
+                    */
+                    LOG.debug("NAT Service : Remove the IP mapping for the router ID {} and internal IP {}", routerId, subnetAddr[0]);
+                    naptManager.removeFromIpMapDS(routerId, subnetAddr[0] + "/" + subnetAddr[1]);
+                }
+            }
+            LOG.debug("NAT Service : End processing of the Subnet IDs removal during the update operation");
+        }
+    }
+
+    private void allocateExternalIp(BigInteger dpnId, long routerId, Uuid networkId, String subnetIp){
+        String leastLoadedExtIpAddr = NatUtil.getLeastLoadedExternalIp(dataBroker, routerId);
+        if (leastLoadedExtIpAddr != null) {
+            String[] externalIpParts = NatUtil.getExternalIpAndPrefix(leastLoadedExtIpAddr);
+            String leastLoadedExtIp = externalIpParts[0];
+            String leastLoadedExtIpPrefix = externalIpParts[1];
+            String leastLoadedExtIpAddrStr = leastLoadedExtIp + "/" + leastLoadedExtIpPrefix;
+            IPAddress externalIpAddr = new IPAddress(leastLoadedExtIp, Integer.parseInt(leastLoadedExtIpPrefix));
+            String[] subnetIpParts = NatUtil.getSubnetIpAndPrefix(subnetIp);
+            subnetIp = subnetIpParts[0];
+            String subnetIpPrefix = subnetIpParts[1];
+            IPAddress subnetIpAddr = new IPAddress(subnetIp, Integer.parseInt(subnetIpPrefix));
+            LOG.debug("NAT Service : Add the IP mapping for the router ID {} and internal IP {} and prefix {} -> external IP {} and prefix {}",
+                    routerId, subnetIp, subnetIpPrefix, leastLoadedExtIp, leastLoadedExtIpPrefix);
+            naptManager.registerMapping(routerId, subnetIpAddr, externalIpAddr);
+
+
+            //Check if external IP is already assigned a route. (i.e. External IP is previously allocated to any of the subnets)
+            //If external IP is already assigned a route, (, do not re-advertise to the BGP
+            if(checkExternalIpLabel(routerId, leastLoadedExtIpAddrStr)){
+                return;
+            }
+
+            //Re-advertise to the BGP for the external IP, which is allocated to the subnet for the first time and hence not having a route.
+            //Get the VPN Name using the network ID
+            final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
+            if (vpnName != null) {
+                LOG.debug("Retrieved vpnName {} for networkId {}", vpnName, networkId);
+                advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, routerId,
+                        leastLoadedExtIp + "/" + leastLoadedExtIpPrefix, vpnService, fibService, bgpManager, dataBroker, LOG);
+            }
+        }
+    }
+
+    private boolean checkExternalIpLabel(long routerId, String externalIp){
+        List<IpMap> ipMaps = naptManager.getIpMapList(dataBroker, routerId);
+        for(IpMap ipMap : ipMaps){
+            if(ipMap.getExternalIp().equals(externalIp)){
+                if (ipMap.getLabel() != null){
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     @Override
@@ -849,35 +1302,100 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
             LOG.info("Removing default NAT route from FIB on all dpns part of router {} ", routerName);
             addOrDelDefFibRouteToSNAT(routerName, false);
             Uuid networkUuid = router.getNetworkId();
-            List<String> externalIps = router.getExternalIps();
-            handleDisableSnat(routerName, networkUuid, externalIps);
+            Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+            if (routerId == NatConstants.INVALID_ID) {
+                LOG.error("NAT Service : Invalid routerId returned for routerName {}", routerName);
+                return;
+            }
+            List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
+            handleDisableSnat(routerName, networkUuid, externalIps, true, null);
         }
     }
 
-    public void handleDisableSnat(String routerName, Uuid networkUuid, List<String> externalIps){
+    public void handleDisableSnat(String routerName, Uuid networkUuid, List<String> externalIps, boolean routerFlag, String vpnId){
         LOG.info("NAT Service : handleDisableSnat() Entry");
-        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        try {
+            Long routerId = NatUtil.getVpnId(dataBroker, routerName);
 
-        BigInteger naptSwitchDpnId = null;
-        InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName);
-        Optional<RouterToNaptSwitch> rtrToNapt = read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch );
-        if(rtrToNapt.isPresent()) {
-            naptSwitchDpnId = rtrToNapt.get().getPrimarySwitchId();
-        }
-        LOG.debug("NAT Service : got primarySwitch as dpnId{} ", naptSwitchDpnId);
+            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);
+            }
 
-        removeNaptFlowsFromActiveSwitch(routerId, routerName, naptSwitchDpnId);
-        removeFlowsFromNonActiveSwitches(routerName, naptSwitchDpnId);
-        advToBgpAndRemoveFibAndTsFlows(naptSwitchDpnId, routerId, networkUuid, externalIps);
+            //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);
 
-        //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) {
+                removeNaptSwitch(routerName);
+            } else {
+                updateNaptSwitch(routerName, BigInteger.ZERO);
+            }
 
+            LOG.debug("NAT Service : Remove the ExternalCounter model for the router ID {}", routerId);
+            naptManager.removeExternalCounter(routerId);
+        } catch (Exception ex) {
+            LOG.error("Exception while handling disableSNAT : {}", ex);
+        }
         LOG.info("NAT Service : handleDisableSnat() Exit");
     }
 
-    public void removeNaptFlowsFromActiveSwitch(long routerId, String routerName, BigInteger dpnId){
+    public void handleDisableSnatInternetVpn(String routerName, Uuid networkUuid, List<String> externalIps, boolean routerFlag, String vpnId){
+        LOG.debug("NAT Service : handleDisableSnatInternetVpn() Entry");
+        try {
+            Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+            BigInteger naptSwitchDpnId = null;
+            InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName);
+            Optional<RouterToNaptSwitch> rtrToNapt = read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch);
+            if (rtrToNapt.isPresent()) {
+                naptSwitchDpnId = rtrToNapt.get().getPrimarySwitchId();
+            }
+            LOG.debug("NAT Service : got primarySwitch as dpnId{} ", naptSwitchDpnId);
+
+            removeNaptFlowsFromActiveSwitchInternetVpn(routerId, routerName, naptSwitchDpnId, networkUuid, vpnId );
+            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 disableSNATInternetVpn : {}", ex);
+        }
+        LOG.debug("NAT Service : handleDisableSnatInternetVpn() Exit");
+    }
+
+    public void updateNaptSwitch(String routerName, BigInteger naptSwitchId) {
+        RouterToNaptSwitch naptSwitch = new RouterToNaptSwitchBuilder().setKey(new RouterToNaptSwitchKey(routerName))
+                .setPrimarySwitchId(naptSwitchId).build();
+        try {
+            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                    NatUtil.buildNaptSwitchRouterIdentifier(routerName), naptSwitch);
+        } catch (Exception ex) {
+            LOG.error("Failed to write naptSwitch {} for router {} in ds",
+                    naptSwitchId,routerName);
+        }
+        LOG.debug("Successfully updated naptSwitch {} for router {} in ds",
+                naptSwitchId,routerName);
+    }
+
+    protected void removeNaptSwitch(String routerName){
+        // Remove router and switch from model
+        InstanceIdentifier<RouterToNaptSwitch> id = InstanceIdentifier.builder(NaptSwitches.class).child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
+        LOG.debug("NAPT Service : Removing NaptSwitch and Router for the router {} from datastore", routerName);
+        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+    }
+
+     public void removeNaptFlowsFromActiveSwitch(long routerId, String routerName, BigInteger dpnId, Uuid networkId, String vpnName){
+
         LOG.debug("NAT Service : Remove NAPT flows from Active switch");
         BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
 
@@ -910,13 +1428,39 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         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);
         mdsalManager.removeFlow(outboundNatFlowEntity);
 
-        //Remove the NAPT PFIB TABLE which forwards the packet to FIB Table
+        //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);
 
         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);
         mdsalManager.removeFlow(natPfibFlowEntity);
 
+        //Long vpnId = NatUtil.getVpnId(dataBroker, routerId); - This does not work since ext-routers is deleted already - no network info
+        //Get the VPN ID from the ExternalNetworks model
+        long vpnId = -1;
+        if( (vpnName == null) || (vpnName.isEmpty()) ) {
+            // ie called from router delete cases
+            Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
+            LOG.debug("NAT Service : vpnUuid is {}", vpnUuid);
+            if(vpnUuid != null) {
+                vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
+                LOG.debug("NAT Service : vpnId for routerdelete or disableSNAT scenario {}", vpnId );
+            }
+        } else {
+            // ie called from disassociate vpn case
+            LOG.debug("NAT Service: This is disassociate nw with vpn case with vpnName {}", vpnName);
+            vpnId = NatUtil.getVpnId(dataBroker, vpnName);
+            LOG.debug("NAT Service : vpnId for disassociate nw with vpn scenario {}", vpnId );
+        }
+
+        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);
+           mdsalManager.removeFlow(natPfibVpnFlowEntity);
+        }
+
         //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
         IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
         if(ipPortMapping == null){
@@ -958,15 +1502,91 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         }
     }
 
-    public void removeFlowsFromNonActiveSwitches(String routerName, BigInteger naptSwitchDpnId){
+     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");
+         BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
+
+         //Remove the NAPT PFIB TABLE entry
+         long vpnId = -1;
+         if(vpnName != null) {
+             // ie called from disassociate vpn case
+             LOG.debug("NAT Service: This is disassociate nw with vpn case with vpnName {}", vpnName);
+             vpnId = NatUtil.getVpnId(dataBroker, vpnName);
+             LOG.debug("NAT Service : vpnId for disassociate nw with vpn scenario {}", vpnId );
+         }
+
+         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);
+            mdsalManager.removeFlow(natPfibVpnFlowEntity);
+
+             // Remove IP-PORT active NAPT entries and release port from IdManager
+             //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
+             IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
+             if(ipPortMapping == null){
+                 LOG.error("NAT Service : Unable to retrieve the IpPortMapping");
+                 return;
+             }
+             List<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType();
+             for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){
+                 List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
+                 for(IpPortMap ipPortMap : ipPortMaps){
+                     String ipPortInternal = ipPortMap.getIpPortInternal();
+                     String[] ipPortParts = ipPortInternal.split(":");
+                     if(ipPortParts.length != 2) {
+                         LOG.error("NAT Service : Unable to retrieve the Internal IP and port");
+                         return;
+                     }
+                     String internalIp = ipPortParts[0];
+                     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);
+
+                     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);
+                     mdsalManager.removeFlow(outboundNaptFlowEntity);
+
+                     IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
+                     String externalIp = ipPortExternal.getIpAddress();
+                     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);
+
+                     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);
+                     mdsalManager.removeFlow(inboundNaptFlowEntity);
+
+                     // Finally release port from idmanager
+                     String internalIpPort = internalIp +":"+internalPort;
+                     naptManager.removePortFromPool(internalIpPort, externalIp);
+
+                     //Remove sessions from models
+                     naptManager.removeIpPortMappingForRouterID(routerId);
+                     naptManager.removeIntIpPortMappingForRouterID(routerId);
+                 }
+             }
+         } else {
+             LOG.error("NAT Service : Invalid vpnId {}", vpnId);
+         }
+     }
+
+    public void removeFlowsFromNonActiveSwitches(String routerName, BigInteger naptSwitchDpnId, Uuid networkId){
         LOG.debug("NAT Service : Remove NAPT related flows from non active switches");
 
         //Remove the flows from the other switches which points to the primary and secondary switches for the flows related the router ID.
-        List<VpnToDpnList> allSwitchList = NatUtil.getVpnToDpnList(dataBroker, routerName);
+        List<BigInteger> allSwitchList = naptSwitchSelector.getDpnsForVpn(routerName);
+        if(allSwitchList == null || allSwitchList.isEmpty()){
+            LOG.error("NAT Service : Unable to get the swithces for the router {}", routerName);
+            return;
+        }
         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
-        for (VpnToDpnList eachSwitch : allSwitchList) {
-            BigInteger dpnId = eachSwitch.getDpnId();
-            if (naptSwitchDpnId != dpnId) {
+        for (BigInteger dpnId : allSwitchList) {
+            if (!naptSwitchDpnId.equals(dpnId)) {
                 LOG.info("NAT Service : Handle Ordinary switch");
 
                 //Remove the PSNAT entry which forwards the packet to Terminating Service table
@@ -988,56 +1608,72 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         }
     }
 
-    public void advToBgpAndRemoveFibAndTsFlows(final BigInteger dpnId, Long routerId, Uuid networkUuid, List<String> externalIps){
+    public void clrRtsFromBgpAndDelFibTs(final BigInteger dpnId, Long routerId, Uuid networkUuid, List<String> externalIps, String vpnName) {
         //Withdraw the corresponding routes from the BGP.
         //Get the network ID using the router ID.
-        LOG.debug("NAT Service : Advertise to BGP and remove routes");
+        LOG.debug("NAT Service : Advertise to BGP and remove routes for externalIps {} with routerId {}, network Id {} and vpnName {}",
+                externalIps,routerId,networkUuid, vpnName);
         if(networkUuid == null ){
             LOG.error("NAT Service : networkId is null");
             return;
         }
 
-        //Get the VPN Name using the network ID
-        final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkUuid, LOG);
-        if (vpnName == null) {
-            LOG.error("No VPN associated with ext nw {} for the router {}",
-                    networkUuid, routerId);
+        if (externalIps == null || externalIps.isEmpty()) {
+            LOG.debug("NAT Service : externalIps is null");
             return;
         }
 
-        //Inform BGP about the route removal
-        String rd = NatUtil.getVpnRd(dataBroker, vpnName);
-        String prefix = "32";
-        NatUtil.removePrefixFromBGP(bgpManager, rd, prefix, LOG);
+        if(vpnName ==null) {
+            //Get the VPN Name using the network ID
+            vpnName = NatUtil.getAssociatedVPN(dataBroker, networkUuid, LOG);
+            if (vpnName == null) {
+                LOG.error("No VPN associated with ext nw {} for the router {}",
+                        networkUuid, routerId);
+                return;
+            }
+        }
+        LOG.debug("Retrieved vpnName {} for networkId {}",vpnName,networkUuid);
 
         //Remove custom FIB routes
         //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
-        final String externalIp = externalIps.get(0);
+        for (String extIp : externalIps) {
+            clrRtsFromBgpAndDelFibTs(dpnId, routerId, extIp, vpnName);
+        }
+    }
 
+    private void clrRtsFromBgpAndDelFibTs(final BigInteger dpnId, long routerId, String extIp, final String vpnName){
+        //Inform BGP about the route removal
+        String rd = NatUtil.getVpnRd(dataBroker, vpnName);
+        NatUtil.removePrefixFromBGP(bgpManager, rd, extIp, LOG);
+
+        LOG.debug("Removing fib entry for externalIp {} in routerId {}",extIp,routerId);
         //Get IPMaps from the DB for the router ID
-        List<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
-        if(dbIpMaps == null ){
-            LOG.error("NAT Service : IPMaps is null");
+        List<IpMap> dbIpMaps = NaptManager.getIpMapList(dataBroker, routerId);
+        if (dbIpMaps == null || dbIpMaps.isEmpty()) {
+            LOG.error("NAT Service : IPMaps not found for router {}",routerId);
             return;
         }
 
-        long tempLabel = -1;
-        for(IpMap dbIpMap: dbIpMaps) {
+        long tempLabel = NatConstants.INVALID_ID;
+        for (IpMap dbIpMap : dbIpMaps) {
             String dbExternalIp = dbIpMap.getExternalIp();
+            LOG.debug("Retrieved dbExternalIp {} for router id {}",dbExternalIp,routerId);
             //Select the IPMap, whose external IP is the IP for which FIB is installed
-            if (externalIp.contains(dbExternalIp)) {
+            if (extIp.equals(dbExternalIp)) {
                 tempLabel = dbIpMap.getLabel();
+                LOG.debug("Retrieved label {} for dbExternalIp {} with router id {}",tempLabel,dbExternalIp,routerId);
                 break;
             }
         }
-        if(tempLabel == -1){
-            LOG.error("NAT Service : Label is null");
+        if (tempLabel < 0 || tempLabel == NatConstants.INVALID_ID) {
+            LOG.error("NAT Service : Label not found for externalIp {} with router id {}",extIp,routerId);
             return;
         }
 
         final long label = tempLabel;
-        RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/" +
-                NatConstants.DEFAULT_PREFIX).setServiceId(label).build();
+        final String externalIp = extIp;
+
+        RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp).setServiceId(label).build();
         Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
 
         ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
@@ -1110,17 +1746,278 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Rou
         LOG.debug("NAT Service : LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
     }
 
-    public static GroupEntity buildGroupEntity(BigInteger dpnId, long groupId) {
-        GroupEntity groupEntity = new GroupEntity(dpnId);
-        groupEntity.setGroupId(groupId);
-        return groupEntity;
-    }
-
     protected InstanceIdentifier<Routers> getWildCardPath()
     {
         return InstanceIdentifier.create(ExtRouters.class).child(Routers.class);
     }
 
+
+    /**
+     * router association to vpn
+     *
+     */
+    public void changeLocalVpnIdToBgpVpnId(String routerName, String bgpVpnName){
+        LOG.debug("NAT Service : Router associated to BGP VPN");
+        if (chkExtRtrAndSnatEnbl(new Uuid(routerName))) {
+            long bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnName);
+
+            LOG.debug("BGP VPN ID value {} ", bgpVpnId);
+
+            if(bgpVpnId != NatConstants.INVALID_ID){
+                LOG.debug("Populate the router-id-name container with the mapping BGP VPN-ID {} -> BGP VPN-NAME {}", bgpVpnId, bgpVpnName);
+                RouterIds rtrs = new RouterIdsBuilder().setKey(new RouterIdsKey(bgpVpnId)).setRouterId(bgpVpnId).setRouterName(bgpVpnName).build();
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getRoutersIdentifier(bgpVpnId), rtrs);
+
+                // Get the allocated Primary NAPT Switch for this router
+                long routerId = NatUtil.getVpnId(dataBroker, routerName);
+                LOG.debug("Router ID value {} ", routerId);
+                BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
+
+                LOG.debug("NAT Service : Update the Router ID {} to the BGP VPN ID {} ", routerId, bgpVpnId);
+                addOrDelDefaultFibRouteForSNATWIthBgpVpn(routerName, bgpVpnId, true);
+
+                // Get the group ID
+                long groupId = createGroupId(getGroupIdKey(routerName));
+                installFlowsWithUpdatedVpnId(primarySwitchId, routerName, groupId, bgpVpnId, routerId);
+            }
+        }
+    }
+
+    /**
+     * router disassociation from vpn
+     *
+     */
+    public void changeBgpVpnIdToLocalVpnId(String routerName, String bgpVpnName){
+        LOG.debug("NAT Service : Router dissociated from BGP VPN");
+        if(chkExtRtrAndSnatEnbl(new Uuid(routerName))) {
+            long bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnName);
+            LOG.debug("BGP VPN ID value {} ", bgpVpnId);
+
+            // Get the allocated Primary NAPT Switch for this router
+            long routerId = NatUtil.getVpnId(dataBroker, routerName);
+            LOG.debug("Router ID value {} ", routerId);
+            BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
+
+            LOG.debug("NAT Service : Update the BGP VPN ID {} to the Router ID {}", bgpVpnId, routerId);
+            addOrDelDefaultFibRouteForSNATWIthBgpVpn(routerName, NatConstants.INVALID_ID, true);
+
+            // Get the group ID
+            long groupId = createGroupId(getGroupIdKey(routerName));
+            installFlowsWithUpdatedVpnId(primarySwitchId, routerName, groupId, NatConstants.INVALID_ID, routerId);
+        }
+    }
+
+    boolean chkExtRtrAndSnatEnbl(Uuid routerUuid){
+        InstanceIdentifier<Routers> routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child
+                (Routers.class, new RoutersKey(routerUuid.getValue())).build();
+        Optional<Routers> routerData = read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInstanceIndentifier);
+        if (routerData.isPresent() && routerData.get().isEnableSnat()) {
+            return true;
+        }
+        return false;
+    }
+
+    public void installFlowsWithUpdatedVpnId(BigInteger primarySwitchId, String routerName, long groupId, long bgpVpnId, long routerId){
+        long changedVpnId = bgpVpnId;
+        String logMsg = "NAT Service : Update the BGP VPN ID {}";
+        if (bgpVpnId == NatConstants.INVALID_ID){
+            changedVpnId = routerId;
+            logMsg = "NAT Service : Update the router ID {}";
+        }
+
+        LOG.debug(logMsg + " in the SNAT miss entry pointing to group {} in the primary switch {}",
+                changedVpnId, groupId, primarySwitchId);
+        FlowEntity flowEntity = buildSnatFlowEntityWithUpdatedVpnId(primarySwitchId, routerName, groupId, changedVpnId);
+        mdsalManager.installFlow(flowEntity);
+
+        LOG.debug(logMsg + " in the Terminating Service table (table ID 36) which forwards the packet" +
+                " to the table 46 in the Primary switch {}",  changedVpnId, primarySwitchId);
+        installTerminatingServiceTblEntryWithUpdatedVpnId(primarySwitchId, routerName, changedVpnId);
+
+        LOG.debug(logMsg + " in the Outbound NAPT table (table ID 46) which punts the packet to the" +
+                " controller in the Primary switch {}", changedVpnId, primarySwitchId);
+        createOutboundTblEntryWithBgpVpn(primarySwitchId, routerId, changedVpnId);
+
+        LOG.debug(logMsg + " in the NAPT PFIB TABLE which forwards the outgoing packet to FIB Table in the Primary switch {}",
+                changedVpnId, primarySwitchId);
+        installNaptPfibEntryWithBgpVpn(primarySwitchId, routerId, changedVpnId);
+
+        LOG.debug(logMsg + " in the NAPT flows for the Outbound NAPT table (table ID 46) and the INBOUND NAPT table (table ID 44)" +
+                        " in the Primary switch {}", changedVpnId, primarySwitchId);
+        updateNaptFlowsWithVpnId(primarySwitchId, routerId, bgpVpnId);
+
+        List<BigInteger> switches = NatUtil.getDpnsForRouter(dataBroker, routerName);
+        for(BigInteger dpnId : switches) {
+            // Update the BGP VPN ID in the SNAT miss entry to group
+            if( !dpnId.equals(primarySwitchId) ) {
+                LOG.debug(logMsg + " in the SNAT miss entry pointing to group {} in the non NAPT switch {}",
+                        changedVpnId, groupId, dpnId);
+                flowEntity = buildSnatFlowEntityWithUpdatedVpnId(dpnId, routerName, groupId, changedVpnId);
+                mdsalManager.installFlow(flowEntity);
+            }
+        }
+    }
+
+    public void updateNaptFlowsWithVpnId(BigInteger dpnId, long routerId, long bgpVpnId){
+        //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
+        IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
+        if(ipPortMapping == null){
+            LOG.error("NAT Service : Unable to retrieve the IpPortMapping");
+            return;
+        }
+
+        List<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType();
+        for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){
+            List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
+            for(IpPortMap ipPortMap : ipPortMaps){
+                String ipPortInternal = ipPortMap.getIpPortInternal();
+                String[] ipPortParts = ipPortInternal.split(":");
+                if(ipPortParts.length != 2) {
+                    LOG.error("NAT Service : Unable to retrieve the Internal IP and port");
+                    return;
+                }
+                String internalIp = ipPortParts[0];
+                String internalPort = ipPortParts[1];
+
+                ProtocolTypes protocolTypes = intextIpProtocolType.getProtocol();
+                NAPTEntryEvent.Protocol protocol;
+                switch (protocolTypes){
+                    case TCP:
+                        protocol = NAPTEntryEvent.Protocol.TCP;
+                        break;
+                    case UDP:
+                        protocol = NAPTEntryEvent.Protocol.UDP;
+                        break;
+                    default:
+                        protocol = NAPTEntryEvent.Protocol.TCP;
+                }
+                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,
+                        internalAddress, externalAddress, protocol);
+                naptEventHandler.buildAndInstallNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, internetVpnid, routerId, bgpVpnId,
+                        externalAddress, internalAddress, protocol);
+
+            }
+        }
+    }
+
+    public FlowEntity buildSnatFlowEntityWithUpdatedVpnId(BigInteger dpId, String routerName, long groupId, long changedVpnId) {
+
+        LOG.debug("NAT Service : buildSnatFlowEntity is called for dpId {}, routerName {} groupId {} changed VPN ID {}", dpId, routerName, groupId, changedVpnId );
+        List<MatchInfo> matches = new ArrayList<>();
+        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 }));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        List<ActionInfo> actionsInfo = new ArrayList<>();
+
+        ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
+                BigInteger.valueOf(changedVpnId)}) ;
+        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,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
+
+        LOG.debug("NAT Service : Returning SNAT Flow Entity {}", flowEntity);
+        return flowEntity;
+    }
+
+    // TODO : Replace this with ITM Rpc once its available with full functionality
+    protected void installTerminatingServiceTblEntryWithUpdatedVpnId(BigInteger dpnId, String routerName, long changedVpnId) {
+        LOG.debug("NAT Service : installTerminatingServiceTblEntryWithUpdatedVpnId called for switch {}, routerName {}, BGP VPN ID {}", dpnId, routerName, changedVpnId);
+        FlowEntity flowEntity = buildTsFlowEntityWithUpdatedVpnId(dpnId, routerName, changedVpnId);
+        mdsalManager.installFlow(flowEntity);
+
+    }
+
+    private FlowEntity buildTsFlowEntityWithUpdatedVpnId(BigInteger dpId, String routerName, long changedVpnId) {
+        LOG.debug("NAT Service : buildTsFlowEntityWithUpdatedVpnId called for switch {}, routerName {}, BGP VPN ID {}", dpId, routerName, changedVpnId);
+        BigInteger routerId = BigInteger.valueOf (NatUtil.getVpnId(dataBroker, routerName));
+        BigInteger bgpVpnIdAsBigInt = BigInteger.valueOf(changedVpnId);
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+        matches.add(new MatchInfo(MatchFieldType.tunnel_id, new  BigInteger[] {bgpVpnIdAsBigInt }));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]
+                { bgpVpnIdAsBigInt, 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,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_TS_TABLE, matches, instructions);
+        return flowEntity;
+    }
+
+    public void createOutboundTblEntryWithBgpVpn(BigInteger dpnId, long routerId, long changedVpnId) {
+        LOG.debug("NAT Service : createOutboundTblEntry called for dpId {} and routerId {}, BGP VPN ID {}", dpnId, routerId, changedVpnId);
+        FlowEntity flowEntity = buildOutboundFlowEntityWithBgpVpn(dpnId, routerId, changedVpnId);
+        LOG.debug("NAT Service : Installing flow {}", flowEntity);
+        mdsalManager.installFlow(flowEntity);
+    }
+
+    protected FlowEntity buildOutboundFlowEntityWithBgpVpn(BigInteger dpId, long routerId, long changedVpnId) {
+        LOG.debug("NAT Service : buildOutboundFlowEntityWithBgpVpn called for dpId {} and routerId {}, BGP VPN ID {}", dpId, routerId, changedVpnId);
+        List<MatchInfo> matches = new ArrayList<>();
+        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}));
+
+        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}));
+
+        String flowRef = getFlowRefOutbound(dpId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
+        BigInteger cookie = getCookieOutboundFlow(routerId);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.OUTBOUND_NAPT_TABLE, flowRef,
+                5, 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);
+        mdsalManager.installFlow(naptPfibFlowEntity);
+    }
+
+    public FlowEntity buildNaptPfibFlowEntityWithUpdatedVpnId(BigInteger dpId, long segmentId, long changedVpnId) {
+
+        LOG.debug("NAT Service : buildNaptPfibFlowEntityWithUpdatedVpnId is called for dpId {}, segmentId {}, BGP VPN ID {}", dpId, segmentId, changedVpnId);
+        List<MatchInfo> matches = new ArrayList<>();
+        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 }));
+
+        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) }));
+        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,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+
+        LOG.debug("NAT Service : Returning NaptPFib Flow Entity {}", flowEntity);
+        return flowEntity;
+    }
+
     @Override
     protected ExternalRoutersListener getDataTreeChangeListener()
     {