X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=natservice%2Fnatservice-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fvpnservice%2Fnatservice%2Finternal%2FExternalRoutersListener.java;h=87b9ee60e165674dd33d0f73854967ba46e62a92;hb=HEAD;hp=3e6db2d01b287ff667ad726d051df8d346f12f7a;hpb=6c57939c03cd970113afb80ac2a668f2f07c06ac;p=vpnservice.git diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java index 3e6db2d0..87b9ee60 100644 --- a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java @@ -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 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 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 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 bucketInfoForNonNaptSwitches = getBucketInfoForNonNaptSwitches(dpnId, primarySwitchId, routerName); + groupId = installGroup(dpnId, routerName, bucketInfoForNonNaptSwitches); + }else{ + LOG.debug("NAT Service : Install group in Primary switch {}", dpnId); + List 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 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 subnetList = null; @@ -310,28 +378,86 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase id = NatUtil.getVpnInstanceOpDataIdentifier(routerName); + addOrDelDefaultFibRouteForSNAT(routerName, create); +/* InstanceIdentifier id = NatUtil.getVpnInstanceOpDataIdentifier(routerName); Optional vpnInstOp = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id); if (vpnInstOp.isPresent()) { - List 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 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 dpnListInVpn = vpnInstOp.getVpnToDpnList(); + List 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 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 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 tunType = TunnelTypeVxlan.class; + RpcResult rpcResult; try { Future> result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder() .setSourceDpid(srcDpId) - .setDestinationDpid(dstDpId).build()); - RpcResult 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 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 getBucketInfoForNonNaptSwitches(BigInteger nonNaptSwitchId, BigInteger primarySwitchId, String routerName) { + List listActionInfoPrimary = new ArrayList<>(); + String ifNamePrimary = getTunnelInterfaceName(nonNaptSwitchId, primarySwitchId); + List 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 getBucketInfoForPrimaryNaptSwitch(){ + List listBucketInfo = new ArrayList<>(); + List 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 matches = new ArrayList(); 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 listActionInfo = new ArrayList<>(); ArrayList 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 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 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 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 customInstructions) { + private void makeLFibTableEntry(BigInteger dpId, long serviceId, long tableId) { List matches = new ArrayList(); 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 instructions = new ArrayList(); List actionsInfos = new ArrayList(); 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 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 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 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 originalExternalIpsList = original.getExternalIps(); + List updatedExternalIpsList = update.getExternalIps(); + Set originalExternalIps = Sets.newHashSet(originalExternalIpsList); + Set updatedExternalIps = Sets.newHashSet(updatedExternalIpsList); + + //Check if the External IPs are added during the update. + SetView 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 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 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 ipMaps = naptManager.getIpMapList(dataBroker, routerId); + List 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 externalPorts = new ArrayList<>(); + Map> protoTypesIntIpPortsMap = new HashMap<>(); + InstanceIdentifier ipPortMappingId = InstanceIdentifier.builder(IntextIpPortMap.class) + .child(IpPortMapping.class, new IpPortMappingKey(routerId)).build(); + Optional ipPortMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, ipPortMappingId); + if (ipPortMapping.isPresent()) { + List intextIpProtocolTypes = ipPortMapping.get().getIntextIpProtocolType(); + for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){ + ProtocolTypes protoType = intextIpProtocolType.getProtocol(); + List ipPortMaps = intextIpProtocolType.getIpPortMap(); + for(IpPortMap ipPortMap : ipPortMaps){ + IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal(); + if(ipPortExternal.getIpAddress().equals(externalIp)){ + externalPorts.add(ipPortExternal.getPortNum()); + List 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>> protoTypesIntIpPorts = protoTypesIntIpPortsMap.entrySet(); + Map> internalIpPortMap = new HashMap<>(); + for(Map.Entry protoTypesIntIpPort : protoTypesIntIpPorts){ + ProtocolTypes protocolType = (ProtocolTypes)protoTypesIntIpPort.getKey(); + List removedInternalIpPorts = (List)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 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 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>> internalIpPorts = internalIpPortMap.entrySet(); + for(Map.Entry> internalIpPort : internalIpPorts) { + String internalIp = internalIpPort.getKey(); + LOG.debug("Remove the NAPT translation entries from Outbound NAPT tables for the removed internal IP {}", internalIp); + List 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 originalSubnetIdsList = original.getSubnetIds(); + List updatedSubnetIdsList = update.getSubnetIds(); + Set originalSubnetIds = Sets.newHashSet(originalSubnetIdsList); + Set updatedSubnetIds = Sets.newHashSet(updatedSubnetIdsList); + SetView 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 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 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 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 externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId); + handleDisableSnat(routerName, networkUuid, externalIps, true, null); } } - public void handleDisableSnat(String routerName, Uuid networkUuid, List externalIps){ + public void handleDisableSnat(String routerName, Uuid networkUuid, List 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 = NatUtil.buildNaptSwitchRouterIdentifier(routerName); - Optional 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 externalIps, boolean routerFlag, String vpnId){ + LOG.debug("NAT Service : handleDisableSnatInternetVpn() Entry"); + try { + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + BigInteger naptSwitchDpnId = null; + InstanceIdentifier routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName); + Optional 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 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 intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType(); + for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){ + List 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 allSwitchList = NatUtil.getVpnToDpnList(dataBroker, routerName); + List 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 externalIps){ + public void clrRtsFromBgpAndDelFibTs(final BigInteger dpnId, Long routerId, Uuid networkUuid, List 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> 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 dbIpMaps = naptManager.getIpMapList(dataBroker, routerId); - if(dbIpMaps == null ){ - LOG.error("NAT Service : IPMaps is null"); + List 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> future = fibService.removeFibEntry(input); ListenableFuture> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction, RpcResult>() { @@ -1110,17 +1746,278 @@ public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase 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 routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child + (Routers.class, new RoutersKey(routerUuid.getValue())).build(); + Optional 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 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 intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType(); + for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){ + List 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 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 instructions = new ArrayList<>(); + List 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 matches = new ArrayList<>(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + matches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {bgpVpnIdAsBigInt })); + + List 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 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 instructions = new ArrayList<>(); + List 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 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 listActionInfo = new ArrayList<>(); + ArrayList 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() {