From 45320706157e8500aaa5e085d05ad4373c289d5f Mon Sep 17 00:00:00 2001 From: Sumanth MS Date: Wed, 13 Apr 2016 17:20:29 +0530 Subject: [PATCH] Support for SNAT and DNAT features in L3 forwarding services. Change-Id: I86c62a4ed639fc8859960f281a8bce7f535ef6a6 Signed-off-by: Sumanth MS --- features/pom.xml | 17 + features/src/main/features/features.xml | 3 + fibmanager/fibmanager-api/pom.xml | 5 + .../fibmanager-api/src/main/yang/fib-rpc.yang | 57 + .../vpnservice/fibmanager/FibConstants.java | 18 + .../fibmanager/FibManagerProvider.java | 16 + .../fibmanager/FibRpcServiceImpl.java | 394 ++++++ .../fibmanager/test/FibManagerTest.java | 4 + natservice/natservice-api/pom.xml | 45 + .../natservice-api/src/main/yang/odl-nat.yang | 138 ++ natservice/natservice-impl/pom.xml | 107 ++ .../src/main/config/default-config.xml | 50 + .../natservice/internal/DpnInVpnListener.java | 216 ++++ .../natservice/internal/EventDispatcher.java | 40 + .../internal/ExternalNetworkListener.java | 235 ++++ .../ExternalNetworksChangeListener.java | 374 ++++++ .../internal/ExternalRoutersListener.java | 1130 +++++++++++++++++ .../internal/FloatingIPHandler.java | 22 + .../internal/FloatingIPListener.java | 649 ++++++++++ .../natservice/internal/IPAddress.java | 26 + .../internal/InterfaceStateEventListener.java | 376 ++++++ .../natservice/internal/NAPTEntryEvent.java | 54 + .../internal/NAPTSwitchSelector.java | 220 ++++ .../natservice/internal/NaptEventHandler.java | 228 ++++ .../internal/NaptFlowRemovedEventHandler.java | 202 +++ .../natservice/internal/NaptManager.java | 556 ++++++++ .../internal/NaptPacketInHandler.java | 118 ++ .../natservice/internal/NaptSwitchHA.java | 661 ++++++++++ .../natservice/internal/NatConstants.java | 52 + .../internal/NatNodeEventListener.java | 94 ++ .../internal/NatServiceProvider.java | 194 +++ .../natservice/internal/NatUtil.java | 700 ++++++++++ .../internal/RouterPortsListener.java | 98 ++ .../internal/SNATDefaultRouteProgrammer.java | 97 ++ .../natservice/internal/SessionAddress.java | 27 + .../internal/VpnFloatingIpHandler.java | 342 +++++ .../impl/rev160111/NATServiceModule.java | 35 + .../rev160111/NATServiceModuleFactory.java | 13 + .../src/main/yang/natservice-impl.yang | 70 + .../ExternalNetworksChangeListenerTest.java | 131 ++ .../internal/test/NaptManagerTest.java | 203 +++ natservice/pom.xml | 49 + pom.xml | 1 + .../src/main/yang/odl-l3vpn.yang | 24 + .../vpnmanager-api/src/main/yang/vpn-rpc.yang | 39 + 45 files changed, 8130 insertions(+) create mode 100644 fibmanager/fibmanager-api/src/main/yang/fib-rpc.yang create mode 100644 fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibConstants.java create mode 100644 fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibRpcServiceImpl.java create mode 100644 natservice/natservice-api/pom.xml create mode 100644 natservice/natservice-api/src/main/yang/odl-nat.yang create mode 100644 natservice/natservice-impl/pom.xml create mode 100644 natservice/natservice-impl/src/main/config/default-config.xml create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/DpnInVpnListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/EventDispatcher.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworkListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworksChangeListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPHandler.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/IPAddress.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/InterfaceStateEventListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTEntryEvent.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTSwitchSelector.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptEventHandler.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptFlowRemovedEventHandler.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptManager.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptPacketInHandler.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptSwitchHA.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatConstants.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatNodeEventListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatServiceProvider.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatUtil.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/RouterPortsListener.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SNATDefaultRouteProgrammer.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SessionAddress.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/VpnFloatingIpHandler.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModule.java create mode 100644 natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModuleFactory.java create mode 100644 natservice/natservice-impl/src/main/yang/natservice-impl.yang create mode 100644 natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/ExternalNetworksChangeListenerTest.java create mode 100644 natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/NaptManagerTest.java create mode 100644 natservice/pom.xml create mode 100644 vpnmanager/vpnmanager-api/src/main/yang/vpn-rpc.yang diff --git a/features/pom.xml b/features/pom.xml index 96b6db0f..2f1140fd 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -356,6 +356,23 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL dhcpservice-api ${vpnservices.version} + + ${project.groupId} + natservice-impl + ${vpnservices.version} + + + ${project.groupId} + natservice-impl + ${vpnservices.version} + config + xml + + + ${project.groupId} + natservice-api + ${vpnservices.version} + ${project.groupId} neutronvpn-api diff --git a/features/src/main/features/features.xml b/features/src/main/features/features.xml index 47e83889..e9d95b7d 100644 --- a/features/src/main/features/features.xml +++ b/features/src/main/features/features.xml @@ -41,6 +41,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mvn:org.opendaylight.vpnservice/itm-api/{{VERSION}} mvn:org.opendaylight.vpnservice/neutronvpn-api/{{VERSION}} mvn:org.opendaylight.vpnservice/dhcpservice-api/{{VERSION}} + mvn:org.opendaylight.vpnservice/natservice-api/{{VERSION}} odl-mdsal-broker @@ -70,6 +71,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mvn:org.opendaylight.vpnservice/dhcpservice-impl/{{VERSION}} mvn:org.opendaylight.vpnservice/elanmanager-api/{{VERSION}} mvn:org.opendaylight.vpnservice/elanmanager-impl/{{VERSION}} + mvn:org.opendaylight.vpnservice/natservice-impl/{{VERSION}} wrap:mvn:org.apache.thrift/libthrift/0.9.1$overwrite=merge&Bundle-Version=0.9.1&Export-Package=*;-noimport:=true;version="0.9.1" @@ -88,6 +90,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mvn:org.opendaylight.vpnservice/neutronvpn-impl/{{VERSION}}/xml/config mvn:org.opendaylight.vpnservice/dhcpservice-impl/{{VERSION}}/xml/config mvn:org.opendaylight.vpnservice/elanmanager-impl/{{VERSION}}/xml/config + mvn:org.opendaylight.vpnservice/natservice-impl/{{VERSION}}/xml/config diff --git a/fibmanager/fibmanager-api/pom.xml b/fibmanager/fibmanager-api/pom.xml index d698dec9..4296f005 100644 --- a/fibmanager/fibmanager-api/pom.xml +++ b/fibmanager/fibmanager-api/pom.xml @@ -54,5 +54,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html org.opendaylight.controller config-api + + org.opendaylight.openflowplugin.model + model-flow-base + ${openflowplugin.version} + diff --git a/fibmanager/fibmanager-api/src/main/yang/fib-rpc.yang b/fibmanager/fibmanager-api/src/main/yang/fib-rpc.yang new file mode 100644 index 00000000..9a76379f --- /dev/null +++ b/fibmanager/fibmanager-api/src/main/yang/fib-rpc.yang @@ -0,0 +1,57 @@ +module fib-rpc { + namespace "urn:opendaylight:vpnservice:fib:rpc"; + prefix "fib-rpc"; + + import ietf-inet-types { + prefix inet; + revision-date "2010-09-24"; + } + + import opendaylight-flow-types { + prefix offlow; + revision-date "2013-10-26"; + } + + revision "2016-01-21" { + description "FIB Servicer RPC Module"; + } + + /* RPCs */ + + rpc create-fib-entry { + description "to install FIB/LFIB/TST routes on specified dpn with given instructions"; + input { + leaf source-dpid { + type uint64; + } + leaf vpn-name { + type string; + } + leaf service-id { + type uint32; + } + leaf ip-address { + type string; + } + uses offlow:instruction-list; + } + } + + rpc remove-fib-entry { + description "to remove FIB/LFIB/TST routes from specified dpn"; + input { + leaf source-dpid { + type uint64; + } + leaf vpn-name { + type string; + } + leaf service-id { + type uint32; + } + leaf ip-address { + type string; + } + } + } +} \ No newline at end of file diff --git a/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibConstants.java b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibConstants.java new file mode 100644 index 00000000..a2cfc76e --- /dev/null +++ b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibConstants.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.fibmanager; + +import java.math.BigInteger; + +public class FibConstants { + static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16); + static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16); + static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16); + static final int DEFAULT_FIB_FLOW_PRIORITY = 10; + static final String FLOWID_PREFIX = "L3."; +} diff --git a/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibManagerProvider.java b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibManagerProvider.java index 382e6172..52e8fd94 100644 --- a/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibManagerProvider.java +++ b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibManagerProvider.java @@ -12,9 +12,12 @@ import java.util.List; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.fibmanager.api.IFibManager; import org.opendaylight.vpnmanager.api.IVpnManager; import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService; @@ -33,6 +36,8 @@ public class FibManagerProvider implements BindingAwareProvider, IFibManager, Au private ItmRpcService itmManager; private OdlInterfaceRpcService interfaceManager; private FibNodeCapableListener fibNcListener; + private RpcProviderRegistry rpcProviderRegistry; + private RpcRegistration rpcRegistration; @Override public void onSessionInitiated(ProviderContext session) { @@ -51,6 +56,8 @@ public class FibManagerProvider implements BindingAwareProvider, IFibManager, Au fibManager.setITMRpcService(itmManager); fibManager.setInterfaceManager(interfaceManager); fibNcListener = new FibNodeCapableListener(dataBroker, fibManager); + FibRpcService fibRpcService = new FibRpcServiceImpl(dataBroker, mdsalManager, this); + rpcRegistration = getRpcProviderRegistry().addRpcImplementation(FibRpcService.class, fibRpcService); } catch (Exception e) { LOG.error("Error initializing services", e); } @@ -109,4 +116,13 @@ public class FibManagerProvider implements BindingAwareProvider, IFibManager, Au public void deleteStaticRoute(String prefix, String rd) { this.vpnmanager.delExtraRoute(prefix, rd, null); } + + public void setRpcProviderRegistry(RpcProviderRegistry rpcProviderRegistry) { + this.rpcProviderRegistry = rpcProviderRegistry; + } + + private RpcProviderRegistry getRpcProviderRegistry() { + return rpcProviderRegistry; + } + } diff --git a/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibRpcServiceImpl.java b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibRpcServiceImpl.java new file mode 100644 index 00000000..c8afe58e --- /dev/null +++ b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibRpcServiceImpl.java @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.fibmanager; + +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.fibmanager.api.IFibManager; +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnId; +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.VpnInstanceOpDataEntryBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; + +import static org.opendaylight.vpnservice.fibmanager.FibConstants.*; + +public class FibRpcServiceImpl implements FibRpcService { + + private static final Logger LOG = LoggerFactory.getLogger(FibRpcServiceImpl.class); + private IMdsalApiManager mdsalManager; + private DataBroker broker; + private IFibManager fibManager; + + public FibRpcServiceImpl(DataBroker broker, IMdsalApiManager mdsalManager, IFibManager fibManager) { + this.broker = broker; + this.mdsalManager = mdsalManager; + this.fibManager = fibManager; + } + + + /** + * to install FIB routes on specified dpn with given instructions + * + */ + public Future> createFibEntry(CreateFibEntryInput input) { + + BigInteger dpnId = input.getSourceDpid(); + String vpnName = input.getVpnName(); + long vpnId = getVpnId(broker, vpnName); + String ipAddress = input.getIpAddress(); + LOG.info("Create custom FIB entry - {} on dpn {} for VPN {} ", ipAddress, dpnId, vpnName); + List instructions = input.getInstruction(); + + makeLocalFibEntry(vpnId, dpnId, ipAddress, instructions); + updateVpnToDpnAssociation(vpnId, dpnId, ipAddress, vpnName); + + return Futures.immediateFuture(RpcResultBuilder.success().build()); + } + + /** + * to remove FIB/LFIB/TST routes from specified dpn + * + */ + public Future> removeFibEntry(RemoveFibEntryInput input) { + + BigInteger dpnId = input.getSourceDpid(); + String vpnName = input.getVpnName(); + long vpnId = getVpnId(broker, vpnName); + long serviceId = input.getServiceId(); + String ipAddress = input.getIpAddress(); + + LOG.info("Delete custom FIB entry - {} on dpn {} for VPN {} ", ipAddress, dpnId, vpnName); + + removeLocalFibEntry(dpnId, vpnId, ipAddress); + //removeLFibTableEntry(dpnId, serviceId); + //removeTunnelTableEntry(dpnId, serviceId); + removeFromVpnDpnAssociation(vpnId, dpnId, ipAddress, vpnName); + + return Futures.immediateFuture(RpcResultBuilder.success().build()); + } + + private void removeLocalFibEntry(BigInteger dpnId, long vpnId, String ipPrefix) { + String values[] = ipPrefix.split("/"); + String ipAddress = values[0]; + int prefixLength = (values.length == 1) ? 0 : Integer.parseInt(values[1]); + LOG.debug("Removing route from DPN. ip {} masklen {}", ipAddress, prefixLength); + InetAddress destPrefix = null; + try { + destPrefix = InetAddress.getByName(ipAddress); + } catch (UnknownHostException e) { + LOG.error("UnknowHostException in removeRoute. Failed to remove Route for ipPrefix {}", ipAddress); + return; + } + List matches = new ArrayList(); + + matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { + BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + if(prefixLength != 0) { + matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] { + destPrefix.getHostAddress(), Integer.toString(prefixLength) })); + } + + String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, vpnId, ipAddress); + + + int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength; + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, + priority, flowRef, 0, 0, + COOKIE_VM_FIB_TABLE, matches, null); + + mdsalManager.removeFlow(dpnId, flowEntity); + + LOG.debug("FIB entry for route {} on dpn {} removed successfully", ipAddress, dpnId); + } + + private void removeLFibTableEntry(BigInteger dpnId, long serviceId) { + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x8847L })); + matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)})); + + String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, ""); + + LOG.debug("removing LFib entry with flow ref {}", flowRef); + + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef, + DEFAULT_FIB_FLOW_PRIORITY, flowRef, 0, 0, + COOKIE_VM_LFIB_TABLE, matches, null); + + mdsalManager.removeFlow(dpnId, flowEntity); + + LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId); + } + + private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) { + LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId); + List mkMatches = new ArrayList(); + // Matching metadata + mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)})); + Flow flowEntity = 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, null); + mdsalManager.removeFlow(dpnId, flowEntity); + LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId); + } + + private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List customInstructions) { + List mkMatches = new ArrayList(); + + LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId); + + 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); + + mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity); + } + + private long getIpAddress(byte[] rawIpAddress) { + return (((rawIpAddress[0] & 0xFF) << (3 * 8)) + ((rawIpAddress[1] & 0xFF) << (2 * 8)) + + ((rawIpAddress[2] & 0xFF) << (1 * 8)) + (rawIpAddress[3] & 0xFF)) & 0xffffffffL; + } + + private void makeLocalFibEntry(long vpnId, BigInteger dpnId, String ipPrefix, List customInstructions) { + String values[] = ipPrefix.split("/"); + String ipAddress = values[0]; + int prefixLength = (values.length == 1) ? 0 : Integer.parseInt(values[1]); + LOG.debug("Adding route to DPN. ip {} masklen {}", ipAddress, prefixLength); + InetAddress destPrefix = null; + try { + destPrefix = InetAddress.getByName(ipAddress); + } catch (UnknownHostException e) { + LOG.error("UnknowHostException in addRoute. Failed to add Route for ipPrefix {}", ipAddress); + return; + } + List matches = new ArrayList(); + + matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { + BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + if(prefixLength != 0) { + matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] { + destPrefix.getHostAddress(), Integer.toString(prefixLength) })); + } + + String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, vpnId, ipAddress); + + + int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength; + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, + priority, flowRef, 0, 0, + COOKIE_VM_FIB_TABLE, matches, customInstructions); + + mdsalManager.installFlow(dpnId, flowEntity); + + LOG.debug("FIB entry for route {} on dpn {} installed successfully", ipAddress, dpnId); + } + + private void makeLFibTableEntry(BigInteger dpId, long serviceId, List customInstructions) { + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + 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); + instructions.add(writeInstruction); + instructions.addAll(customInstructions); + + // Install the flow entry in L3_LFIB_TABLE + String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, ""); + + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef, + DEFAULT_FIB_FLOW_PRIORITY, flowRef, 0, 0, + COOKIE_VM_LFIB_TABLE, matches, instructions); + + mdsalManager.installFlow(dpId, flowEntity); + + LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId ); + } + + private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) { + return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR) + .append(tableId).append(NwConstants.FLOWID_SEPARATOR) + .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString(); + } + + private synchronized void updateVpnToDpnAssociation(long vpnId, BigInteger dpnId, String ipAddr, String vpnName) { + LOG.debug("Updating VPN to DPN list for dpn : {} for VPN: {} with ip: {}", + dpnId, vpnName, ipAddr); + String routeDistinguisher = getVpnRd(broker, vpnName); + String rd = (routeDistinguisher == null) ? vpnName : routeDistinguisher; + InstanceIdentifier id = getVpnToDpnListIdentifier(rd, dpnId); + Optional dpnInVpn = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id); + org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses + ipAddress = new IpAddressesBuilder().setIpAddress(ipAddr).build(); + + if (dpnInVpn.isPresent()) { + MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance + .op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses.class, + new IpAddressesKey(ipAddr)), ipAddress); + } else { + MDSALUtil.syncUpdate(broker, LogicalDatastoreType.OPERATIONAL, + getVpnInstanceOpDataIdentifier(rd), + getVpnInstanceOpData(rd, vpnId)); + VpnToDpnListBuilder vpnToDpnList = new VpnToDpnListBuilder().setDpnId(dpnId); + List ipAddresses = new ArrayList<>(); + ipAddresses.add(ipAddress); + MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id, + vpnToDpnList.setIpAddresses(ipAddresses).build()); + LOG.debug("populate FIB on new dpn {} for VPN {}", dpnId, vpnName); + fibManager.populateFibOnNewDpn(dpnId, vpnId, rd); + } + } + + private synchronized void removeFromVpnDpnAssociation(long vpnId, BigInteger dpnId, String ipAddr, String vpnName) { + LOG.debug("Removing association of VPN to DPN list for dpn : {} for VPN: {} with ip: {}", + dpnId, vpnName, ipAddr); + String routeDistinguisher = getVpnRd(broker, vpnName); + String rd = (routeDistinguisher == null) ? vpnName : routeDistinguisher; + InstanceIdentifier id = getVpnToDpnListIdentifier(rd, dpnId); + Optional dpnInVpn = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id); + if (dpnInVpn.isPresent()) { + List ipAddresses = dpnInVpn.get().getIpAddresses(); + org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses + ipAddress = new IpAddressesBuilder().setIpAddress(ipAddr).build(); + + if (ipAddresses != null && ipAddresses.remove(ipAddress)) { + if (ipAddresses.isEmpty()) { + List vpnInterfaces = dpnInVpn.get().getVpnInterfaces(); + if(vpnInterfaces ==null || vpnInterfaces.isEmpty()) { + //Clean up the dpn + LOG.debug("Cleaning up dpn {} from VPN {}", dpnId, vpnName); + MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id); + fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd); + } + } else { + delete(broker, LogicalDatastoreType.OPERATIONAL, id.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data + .vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses.class, + new IpAddressesKey(ipAddr))); + } + } + } + } + + //TODO: Below Util methods to be removed once VpnUtil methods are exposed in api bundle + public static String getVpnRd(DataBroker broker, String vpnName) { + + InstanceIdentifier id + = getVpnInstanceToVpnIdIdentifier(vpnName); + Optional vpnInstance + = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id); + + String rd = null; + if(vpnInstance.isPresent()) { + rd = vpnInstance.get().getVrfId(); + } + return rd; + } + + static InstanceIdentifier + getVpnInstanceToVpnIdIdentifier(String vpnName) { + return InstanceIdentifier.builder(VpnInstanceToVpnId.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance.class, + new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceKey(vpnName)).build(); + } + + + static InstanceIdentifier getVpnToDpnListIdentifier(String rd, BigInteger dpnId) { + return InstanceIdentifier.builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd)) + .child(VpnToDpnList.class, new VpnToDpnListKey(dpnId)).build(); + } + + static InstanceIdentifier getVpnInstanceOpDataIdentifier(String rd) { + return InstanceIdentifier.builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd)).build(); + } + + static VpnInstanceOpDataEntry getVpnInstanceOpData(String rd, long vpnId) { + return new VpnInstanceOpDataEntryBuilder().setVrfId(rd).setVpnId(vpnId).build(); + } + + static void delete(DataBroker broker, LogicalDatastoreType datastoreType, + InstanceIdentifier path) { + WriteTransaction tx = broker.newWriteOnlyTransaction(); + tx.delete(datastoreType, path); + tx.submit(); + } + + static long getVpnId(DataBroker broker, String vpnName) { + + InstanceIdentifier id + = getVpnInstanceToVpnIdIdentifier(vpnName); + Optional vpnInstance + = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id); + + long vpnId = -1; + if(vpnInstance.isPresent()) { + vpnId = vpnInstance.get().getVpnId(); + } + return vpnId; + } + +} diff --git a/fibmanager/fibmanager-impl/src/test/java/org/opendaylight/vpnservice/fibmanager/test/FibManagerTest.java b/fibmanager/fibmanager-impl/src/test/java/org/opendaylight/vpnservice/fibmanager/test/FibManagerTest.java index 89841010..84487410 100644 --- a/fibmanager/fibmanager-impl/src/test/java/org/opendaylight/vpnservice/fibmanager/test/FibManagerTest.java +++ b/fibmanager/fibmanager-impl/src/test/java/org/opendaylight/vpnservice/fibmanager/test/FibManagerTest.java @@ -34,6 +34,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instanc import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses; import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables; @@ -133,6 +134,9 @@ public class FibManagerTest { return null; } + @Override + public List getIpAddresses() { return null; } + @Override public VpnToDpnListKey getKey() { return new VpnToDpnListKey(Dpn); diff --git a/natservice/natservice-api/pom.xml b/natservice/natservice-api/pom.xml new file mode 100644 index 00000000..5d8b0c8a --- /dev/null +++ b/natservice/natservice-api/pom.xml @@ -0,0 +1,45 @@ + + + + + org.opendaylight.vpnservice + config-parent + 0.3.0-SNAPSHOT + ../../commons/config-parent + + + 4.0.0 + org.opendaylight.vpnservice + natservice-api + ${vpnservices.version} + bundle + + + + org.opendaylight.mdsal + yang-binding + + + org.opendaylight.yangtools + yang-common + + + org.opendaylight.mdsal.model + ietf-inet-types + + + org.opendaylight.mdsal.model + ietf-yang-types-20130715 + + + org.opendaylight.controller + config-api + + + diff --git a/natservice/natservice-api/src/main/yang/odl-nat.yang b/natservice/natservice-api/src/main/yang/odl-nat.yang new file mode 100644 index 00000000..b56bda21 --- /dev/null +++ b/natservice/natservice-api/src/main/yang/odl-nat.yang @@ -0,0 +1,138 @@ +module odl-nat { + namespace "urn:opendaylight:vpnservice:natservice"; + prefix odl-nat; + + import ietf-yang-types { prefix "yang"; /*revision-date 2013-07-15; */} + import ietf-inet-types { prefix "inet"; } + + revision "2016-01-11" { + description "NAT Manager module"; + } + + container external-networks { + list networks { + key id; + leaf id { + type yang:uuid; + } + leaf vpnid { type yang:uuid; } + leaf-list router-ids { type yang:uuid; } + } + } + + container ext-routers { + list routers { + key router-name; + leaf router-name { type string; } + leaf network-id { type yang:uuid; } + leaf enable-snat { type boolean; } + leaf-list external-ips { + type string; //format - ipaddress\prefixlength + } + leaf-list subnet-ids { type yang:uuid; } + leaf ext_gw_mac_address { type string; } + } + } + + + grouping external-interface-info { + leaf internal-ip { type string; } + leaf external-ip { type string; } + leaf label { type uint16; config false; } + } + + container floating-ip-info { + config true; + list router-ports { + key router-id; + leaf router-id { type string; } + leaf external-network-id { type yang:uuid; } + list ports { + key port-name; + leaf port-name { type string; } + list ip-mapping { + key "internal-ip"; + uses external-interface-info; + } + } + } + } + + container napt-switches { + list router-to-napt-switch { + key router-name; + leaf router-name { type string; } + leaf primary-switch-id { type uint64; } + } + } + + grouping ip-port-entity { + leaf ip-address { type string; } + leaf port-num { type uint16; } + } + + typedef protocol-types { + type enumeration { + enum TCP; + enum UDP; + } + } + + container intext-ip-port-map { + config true; + list ip-port-mapping { + key router-id; + leaf router-id { type uint32; } + list intext-ip-protocol-type { + key protocol; + leaf protocol { type protocol-types; } + list ip-port-map { + key ip-port-internal; + description "internal to external ip-port mapping"; + leaf ip-port-internal { type string; } + container ip-port-external { + uses ip-port-entity; + } + } + } + } + } + + container snatint-ip-port-map { + list intip-port-map { + key router-id; + leaf router-id { type uint32; } + list ip-port { + key internal-ip; + leaf internal-ip { type string; } + list int-ip-proto-type { + key protocol; + leaf protocol { type protocol-types; } + leaf-list ports { type uint16; } + } + } + } + } + + container intext-ip-map { + config false; + list ip-mapping { + key segment-id; + leaf segment-id { type uint32; } + list ip-map { + key internal-ip; + leaf internal-ip { type string; } + leaf external-ip { type string; } + leaf label {type uint32;} + } + } + } + + container router-id-name { + list routerIds { + key router-id; + leaf router-id {type uint32;} + leaf router-name { type string; } + } + } +} diff --git a/natservice/natservice-impl/pom.xml b/natservice/natservice-impl/pom.xml new file mode 100644 index 00000000..d40b99b4 --- /dev/null +++ b/natservice/natservice-impl/pom.xml @@ -0,0 +1,107 @@ + + + + + + org.opendaylight.vpnservice + config-parent + 0.3.0-SNAPSHOT + ../../commons/config-parent + + + 4.0.0 + org.opendaylight.vpnservice + natservice-impl + ${vpnservices.version} + bundle + + 1.6.4 + 1.10.19 + + + + org.opendaylight.vpnservice + natservice-api + ${vpnservices.version} + + + ${project.groupId} + mdsalutil-api + ${vpnservices.version} + + + org.opendaylight.vpnservice + itm-api + ${vpnservices.version} + + + ${project.groupId} + bgpmanager-api + ${vpnservices.version} + + + org.opendaylight.vpnservice + vpnmanager-api + ${vpnservices.version} + + + org.opendaylight.vpnservice + fibmanager-api + ${vpnservices.version} + + + org.opendaylight.vpnservice + idmanager-api + ${vpnservices.version} + + + ${project.groupId} + interfacemgr-api + ${vpnservices.version} + + + ${project.groupId} + neutronvpn-api + ${vpnservices.version} + + + org.opendaylight.openflowplugin.model + model-flow-service + ${openflowplugin.version} + + + org.opendaylight.controller + sal-binding-broker-impl + + + commons-net + commons-net + + + + org.mockito + mockito-all + ${mockitoall.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + + diff --git a/natservice/natservice-impl/src/main/config/default-config.xml b/natservice/natservice-impl/src/main/config/default-config.xml new file mode 100644 index 00000000..4f5f3574 --- /dev/null +++ b/natservice/natservice-impl/src/main/config/default-config.xml @@ -0,0 +1,50 @@ + + + + + + urn:opendaylight:params:xml:ns:yang:natservice:impl?module=natservice-impl&revision=2016-01-11 + urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&revision=2015-04-10 + urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 + urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28 + urn:opendaylight:params:xml:ns:yang:bgpmanager:api?module=bgpmanager-api&revision=2015-04-20 + urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&revision=2015-04-10 + + + + + + + prefix:natservice-impl + natservice-default + + binding:binding-broker-osgi-registry + binding-osgi-broker + + + binding:binding-rpc-registry + binding-rpc-broker + + + bgpmanager:bgpmanager-api + bgpmanager + + + bindingimpl:binding-new-notification-service + binding-notification-adapter + + + mdsalutil:odl-mdsalutil + mdsalutil-service + + + + + + diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/DpnInVpnListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/DpnInVpnListener.java new file mode 100644 index 00000000..d033b80c --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/DpnInVpnListener.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.*; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AddDpnEvent; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.add.dpn.event.AddEventData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.RemoveDpnEvent; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.remove.dpn.event.RemoveEventData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.OdlL3vpnListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; +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.napt.switches.RouterToNaptSwitch; +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.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +public class DpnInVpnListener implements OdlL3vpnListener { + private static final Logger LOG = LoggerFactory.getLogger(DpnInVpnListener.class); + private DataBroker dataBroker; + private SNATDefaultRouteProgrammer defaultRouteProgrammer; + private NaptSwitchHA naptSwitchHA; + private IMdsalApiManager mdsalManager; + private IdManagerService idManager; + + public DpnInVpnListener(DataBroker dataBroker) { + this.dataBroker = dataBroker; + } + + void setDefaultProgrammer(SNATDefaultRouteProgrammer defaultRouteProgrammer) { + this.defaultRouteProgrammer = defaultRouteProgrammer; + } + + void setNaptSwitchHA(NaptSwitchHA switchHA) { + naptSwitchHA = switchHA; + } + + void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + public void setIdManager(IdManagerService idManager) { + this.idManager = idManager; + } + + public void onAddDpnEvent(AddDpnEvent notification) { + AddEventData eventData = notification.getAddEventData(); + BigInteger dpnId = eventData.getDpnId(); + String vpnName = eventData.getVpnName(); + LOG.info("Received add dpn {} in vpn {} event", dpnId, vpnName); + String routerId = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName); + if (routerId != null) { + //check router is associated to external network + InstanceIdentifier id = NatUtil.buildRouterIdentifier(routerId); + Optional routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id); + if (routerData.isPresent()) { + Uuid networkId = routerData.get().getNetworkId(); + if(networkId != null) { + LOG.debug("Router {} is associated with ext nw {}", routerId, networkId); + long vpnId = NatUtil.readVpnId(dataBroker, vpnName); + if(vpnId != NatConstants.INVALID_ID) { + //Install default entry in FIB to SNAT table + LOG.debug("Installing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName); + defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId); + } else { + LOG.debug("Add DPN Event: Could not read vpnId for vpnName {}", vpnName); + } + if (routerData.get().isEnableSnat()) { + LOG.info("SNAT enabled for router {}", routerId); + handleSNATForDPN(dpnId, routerId); + } else { + LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId); + } + } + } + } + } + + void handleSNATForDPN(BigInteger dpnId, String routerName) { + //Check if primary and secondary switch are selected, If not select the role + //Install select group to NAPT switch + //Install default miss entry to NAPT switch + BigInteger naptSwitch; + try { + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + if (routerId == NatConstants.INVALID_ID) { + LOG.error("Invalid routerId returned for routerName {}",routerName); + return; + } + BigInteger naptId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId); + if (naptId == null) { + LOG.debug("No Naptswitch is selected for router {}", routerName); + + naptSwitch = dpnId; + boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch); + if(!naptstatus) { + LOG.error("Failed to update newNaptSwitch {} for routername {}",naptSwitch,routerName); + return; + } + LOG.debug("Switch {} is elected as NaptSwitch for router {}", dpnId, routerName); + + //installing group + List bucketInfo = naptSwitchHA.handleGroupInPrimarySwitch(); + naptSwitchHA.installSnatGroupEntry(naptSwitch,bucketInfo,routerName); + + } else { + LOG.debug("Napt switch is already elected for router {}" + , naptId, routerName); + naptSwitch = naptId; + + //installing group + List bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId, routerName, naptSwitch); + if (bucketInfo == null) { + return; + } + naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName); + + } + // Install miss entry pointing to group + long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager); + FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId,NatConstants.ADD_FLOW); + if (flowEntity == null) { + LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",routerName,dpnId,groupId); + return; + } + LOG.debug("Sucessfully installed flow for dpnId {} router {} group {}",dpnId,routerName,groupId); + mdsalManager.installFlow(flowEntity); + } catch (Exception ex) { + LOG.error("Exception in handleSNATForDPN method : {}",ex); + } + } + + public void onRemoveDpnEvent(RemoveDpnEvent notification) { + RemoveEventData eventData = notification.getRemoveEventData(); + BigInteger dpnId = eventData.getDpnId(); + String vpnName = eventData.getVpnName(); + LOG.info("Received remove dpn {} in vpn {} event", dpnId, vpnName); + String routerId = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName); + if (routerId != null) { + //check router is associated to external network + InstanceIdentifier id = NatUtil.buildRouterIdentifier(routerId); + Optional routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id); + if (routerData.isPresent()) { + Uuid networkId = routerData.get().getNetworkId(); + if(networkId != null) { + LOG.debug("Router {} is associated with ext nw {}", routerId, networkId); + long vpnId = NatUtil.readVpnId(dataBroker, vpnName); + if(vpnId != NatConstants.INVALID_ID) { + //Remove default entry in FIB + LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName); + defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId); + } else { + LOG.debug("Remove DPN Event: Could not read vpnId for vpnName {}", vpnName); + } + if (routerData.get().isEnableSnat()) { + LOG.info("SNAT enabled for router {}", routerId); + removeSNATFromDPN(dpnId,routerId); + } else { + LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId); + } + } + } + } + } + + void removeSNATFromDPN(BigInteger dpnId, String routerName) { + //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed + //remove miss entry to NAPT switch + long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager); + FlowEntity flowEntity = null; + try { + flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, NatConstants.DEL_FLOW); + if (flowEntity == null) { + LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",routerName,dpnId,groupId); + return; + } + LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}",flowEntity); + mdsalManager.removeFlow(flowEntity); + + } catch (Exception ex) { + LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",flowEntity,ex); + return; + } + LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}", dpnId, routerName); + + //remove group + try { + GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, + GroupTypes.GroupAll, null); + LOG.info("NAT Service : Removing NAPT GroupEntity:{}", groupEntity); + mdsalManager.removeGroup(groupEntity); + } catch (Exception ex) { + LOG.debug("NAT Service : Failed to remove group entity {} : {}",flowEntity,ex); + return; + } + LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}", dpnId, routerName); + } +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/EventDispatcher.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/EventDispatcher.java new file mode 100644 index 00000000..d0aad9a0 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/EventDispatcher.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +import java.util.concurrent.BlockingQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EventDispatcher implements Runnable { + private BlockingQueue eventQueue; + private NaptEventHandler naptEventHandler; + private static final Logger LOG = LoggerFactory.getLogger(NaptManager.class); + + EventDispatcher(BlockingQueue eventQueue, NaptEventHandler naptEventHandler){ + this.eventQueue = eventQueue; + this.naptEventHandler = naptEventHandler; + } + + public void addNaptEvent(NAPTEntryEvent naptEntryEvent){ + this.eventQueue.add(naptEntryEvent); + } + + public void run(){ + while(true) { + try { + NAPTEntryEvent event = eventQueue.take(); + naptEventHandler.handleEvent(event); + } catch (InterruptedException e) { + LOG.error("EventDispatcher : Error in handling the event queue : ", e.getMessage()); + e.printStackTrace(); + } + } + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworkListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworkListener.java new file mode 100644 index 00000000..621aea2a --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworkListener.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; + +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.binding.DataObject; +import com.google.common.base.Optional; + +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.VpnInstanceOpDataEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.ArrayList; + + +public class ExternalNetworkListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener implements AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(ExternalNetworkListener.class); + private ListenerRegistration listenerRegistration; + private final DataBroker broker; + private IMdsalApiManager mdsalManager; + + public ExternalNetworkListener (final DataBroker db) { + super(Networks.class); + broker = db; + //registerListener(db); + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + LOG.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + LOG.info("ExternalNetwork Listener Closed"); + } + + private void registerListener(final DataBroker db) { + try { + listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, + getWildCardPath(), ExternalNetworkListener.this, AsyncDataBroker.DataChangeScope.SUBTREE); + } catch (final Exception e) { + LOG.error("External Network DataChange listener registration fail!", e); + throw new IllegalStateException("External Network registration Listener failed.", e); + } + } + + private InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(ExternalNetworks.class).child(Networks.class); + } + + public void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + @Override + protected void add(final InstanceIdentifier identifier, + final Networks nw) { + LOG.trace("External Network add mapping method - key: " + identifier + ", value=" + nw ); + processExternalNwAdd(identifier, nw); + } + + @Override + protected void remove(InstanceIdentifier identifier, Networks nw) { + LOG.trace("External Network remove mapping method - key: " + identifier + ", value=" + nw ); + processExternalNwDel(identifier, nw); + } + + @Override + protected void update(InstanceIdentifier identifier, Networks original, Networks update) { + LOG.trace("External Network update mapping method - key: " + identifier + ", original=" + original + ", update=" + update ); + //check if a new router has been added or an already existing router has been deleted from the external nw to router association + List oldRtrs = original.getRouterIds(); + List newRtrs = update.getRouterIds(); + if (oldRtrs != newRtrs) { + //handle both addition and removal of routers + for (Uuid rtr : newRtrs) { + if (oldRtrs.contains(rtr)) { + oldRtrs.remove(rtr); + } else { + // new router case + //Routers added need to have the corresponding default Fib entry added to the switches in the router + String routerId = rtr.getValue(); + addOrDelDefFibRouteToSNAT(routerId, true); + + } + } + + //Routers removed need to have the corresponding default Fib entry removed from the switches in the router + for (Uuid rtr : oldRtrs) { + String routerId = rtr.getValue(); + addOrDelDefFibRouteToSNAT(routerId, false); + } + } + } + + private void processExternalNwAdd(final InstanceIdentifier identifier, + final Networks network) { + LOG.trace("Add event - key: {}, value: {}", identifier, network); + List routerList = network.getRouterIds(); + + if(routerList == null) { + LOG.debug("No routers associated with external network {}", identifier); + return; + } + + for(Uuid router: routerList) { + String routerId = router.getValue(); + addOrDelDefFibRouteToSNAT(routerId, true); + } + } + + private void processExternalNwDel(final InstanceIdentifier identifier, + final Networks network) { + LOG.trace("Add event - key: {}, value: {}", identifier, network); + List routerList = network.getRouterIds(); + + for(Uuid router: routerList) { + String routerId = router.getValue(); + addOrDelDefFibRouteToSNAT(routerId, false); + } + } + + private void addOrDelDefFibRouteToSNAT(String routerId, boolean create) { + //Router ID is used as the internal VPN's name, hence the vrf-id in VpnInstance Op DataStore + InstanceIdentifier id = NatUtil.getVpnInstanceOpDataIdentifier(routerId); + Optional vpnInstOp = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id); + if (vpnInstOp.isPresent()) { + List dpnListInVpn = vpnInstOp.get().getVpnToDpnList(); + for (VpnToDpnList dpn : dpnListInVpn) { + BigInteger dpnId = dpn.getDpnId(); + long vpnId = NatUtil.readVpnId(broker, vpnInstOp.get().getVrfId()); + if (create == true) { + installDefNATRouteInDPN(dpnId, vpnId); + } else { + removeDefNATRouteInDPN(dpnId, vpnId); + } + } + } + } + + private FlowEntity buildDefNATFlowEntity(BigInteger dpId, long vpnId) { + + InetAddress defaultIP = null; + + try { + defaultIP = InetAddress.getByName("0.0.0.0"); + + } catch (UnknownHostException e) { + LOG.error("UnknowHostException in buildDefNATFlowEntity. Failed to build FIB Table Flow for Default Route to NAT table "); + return null; + } + + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + //add match for default route "0.0.0.0/0" + //matches.add(new MatchInfo(MatchFieldType.ipv4_src, new long[] { + // NatUtil.getIpAddress(defaultIP.getAddress()), 0 })); + + //add match for vrfid + matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { + BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + + List instructions = new ArrayList(); + instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PSNAT_TABLE })); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.L3_FIB_TABLE, defaultIP); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.L3_FIB_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, matches, instructions); + + return flowEntity; + + + } + + private void installDefNATRouteInDPN(BigInteger dpnId, long vpnId) { + FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId); + if(flowEntity == null) { + LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow"); + return; + } + mdsalManager.installFlow(flowEntity); + } + + private void removeDefNATRouteInDPN(BigInteger dpnId, long vpnId) { + FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId); + if(flowEntity == null) { + LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow"); + return; + } + mdsalManager.removeFlow(flowEntity); + } + + +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworksChangeListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworksChangeListener.java new file mode 100644 index 00000000..3207b5c7 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworksChangeListener.java @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +import com.google.common.base.Optional; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.datastoreutils.AsyncDataTreeChangeListenerBase; +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +import org.opendaylight.vpnservice.mdsalutil.BucketInfo; +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.GroupEntity; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExtRouters; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.NaptSwitches; +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; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchKey; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.opendaylight.bgpmanager.api.IBgpManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService; + +/** + * Created by ESUMAMS on 1/21/2016. + */ +public class ExternalNetworksChangeListener extends AsyncDataTreeChangeListenerBase +{ + private static final Logger LOG = LoggerFactory.getLogger( ExternalNetworksChangeListener.class); + + private ListenerRegistration listenerRegistration; + private final DataBroker dataBroker; + private IMdsalApiManager mdsalManager; + //private VpnFloatingIpHandler vpnFloatingIpHandler; + private FloatingIPListener floatingIpListener; + private ExternalRoutersListener externalRouterListener; + private OdlInterfaceRpcService interfaceManager; + private NaptManager naptManager; + + private IBgpManager bgpManager; + private VpnRpcService vpnService; + private FibRpcService fibService; + + + private ExternalRoutersListener externalRoutersListener; + + void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + void setInterfaceManager(OdlInterfaceRpcService interfaceManager) { + this.interfaceManager = interfaceManager; + } + + void setFloatingIpListener(FloatingIPListener floatingIpListener) { + this.floatingIpListener = floatingIpListener; + } + + void setExternalRoutersListener(ExternalRoutersListener externalRoutersListener) { + this.externalRouterListener = externalRoutersListener; + } + + public void setBgpManager(IBgpManager bgpManager) { + this.bgpManager = bgpManager; + } + + public void setNaptManager(NaptManager naptManager) { + this.naptManager = naptManager; + } + + public void setVpnService(VpnRpcService vpnService) { + this.vpnService = vpnService; + } + + public void setFibService(FibRpcService fibService) { + this.fibService = fibService; + } + + public void setListenerRegistration(ListenerRegistration listenerRegistration) { + this.listenerRegistration = listenerRegistration; + } + + public ExternalNetworksChangeListener(final DataBroker dataBroker ) { + super( Networks.class, ExternalNetworksChangeListener.class ); + this.dataBroker = dataBroker; + } + + + protected InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(ExternalNetworks.class).child(Networks.class); + } + + + @Override + protected void add(InstanceIdentifier identifier, Networks networks) { + + } + + @Override + protected ExternalNetworksChangeListener getDataTreeChangeListener() { + return ExternalNetworksChangeListener.this; + } + + @Override + protected void remove(InstanceIdentifier identifier, Networks networks) { + if( identifier == null || networks == null || networks.getRouterIds().isEmpty() ) { + LOG.info( "ExternalNetworksChangeListener:remove:: returning without processing since networks/identifier is null" ); + return; + } + + for( Uuid routerId: networks.getRouterIds() ) { + String routerName = routerId.toString(); + + InstanceIdentifier routerToNaptSwitchInstanceIdentifier = + getRouterToNaptSwitchInstanceIdentifier( routerName); + + MDSALUtil.syncDelete( dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitchInstanceIdentifier ); + + LOG.debug( "ExternalNetworksChangeListener:delete:: successful deletion of data in napt-switches container" ); + } + } + + private static InstanceIdentifier getRouterToNaptSwitchInstanceIdentifier( String routerName ) { + + return InstanceIdentifier.builder( NaptSwitches.class ) + .child( RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build(); + + } + + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } + catch (final Exception e) { + LOG.error("Error when cleaning up ExternalNetworksChangeListener.", e); + } + + listenerRegistration = null; + } + LOG.debug("ExternalNetworksChangeListener Closed"); + } + + + @Override + protected void update(InstanceIdentifier identifier, Networks original, Networks update) { + //Check for VPN disassociation + Uuid originalVpn = original.getVpnid(); + Uuid updatedVpn = update.getVpnid(); + if(originalVpn == null && updatedVpn != null) { + //external network is dis-associated from L3VPN instance + associateExternalNetworkWithVPN(update); + //Install the VPN related FIB entries + installVpnFibEntries(update, updatedVpn.getValue()); + } else if(originalVpn != null && updatedVpn == null) { + //external network is associated with vpn + disassociateExternalNetworkFromVPN(update, originalVpn.getValue()); + //Remove the SNAT entries + removeSnatEntries(original, original.getId()); + } + } + + private void installVpnFibEntries(Networks update, String vpnName){ + List routerUuids = update.getRouterIds(); + for(Uuid routerUuid :routerUuids){ + InstanceIdentifier routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child + (Routers.class, new RoutersKey(routerUuid.getValue())).build(); + Optional routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInstanceIndentifier); + if(!routerData.isPresent()){ + continue; + } + String routerName = routerData.get().getRouterName(); + List externalIps = routerData.get().getExternalIps(); + InstanceIdentifier routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName); + Optional rtrToNapt = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch); + if(!rtrToNapt.isPresent()) { + LOG.debug("Unable to retrieve the Primary switch DPN ID"); + continue; + } + BigInteger naptSwitchDpnId = rtrToNapt.get().getPrimarySwitchId(); + for(String externalIp: externalIps) { + externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitchDpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, NatUtil.getVpnId(dataBroker, routerName), externalIp, + vpnService, fibService, bgpManager, dataBroker, LOG); + } + } + } + + private void removeSnatEntries(Networks original, Uuid networkUuid){ + List routerUuids = original.getRouterIds(); + for(Uuid routerUuid :routerUuids){ + InstanceIdentifier routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child + (Routers.class, new RoutersKey(routerUuid.getValue())).build(); + Optional routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInstanceIndentifier); + List externalIps = null; + if(!routerData.isPresent()){ + continue; + } + externalIps = routerData.get().getExternalIps(); + externalRouterListener.handleDisableSnat(routerUuid.getValue(), networkUuid, externalIps); + } + } + + private void associateExternalNetworkWithVPN(Networks network) { + List routerIds = network.getRouterIds(); + for(Uuid routerId : routerIds) { + //long router = NatUtil.getVpnId(dataBroker, routerId.getValue()); + + InstanceIdentifier routerPortsId = NatUtil.getRouterPortsId(routerId.getValue()); + Optional optRouterPorts = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsId); + if(!optRouterPorts.isPresent()) { + LOG.debug("Could not read Router Ports data object with id: {} to handle associate ext nw {}", routerId, network.getId()); + continue; + } + RouterPorts routerPorts = optRouterPorts.get(); + List interfaces = routerPorts.getPorts(); + for(Ports port : interfaces) { + String portName = port.getPortName(); + BigInteger dpnId = getDpnForInterface(interfaceManager, portName); + if(dpnId.equals(BigInteger.ZERO)) { + LOG.debug("DPN not found for {}, skip handling of ext nw {} association", portName, network.getId()); + continue; + } + List ipMapping = port.getIpMapping(); + for(IpMapping ipMap : ipMapping) { + String externalIp = ipMap.getExternalIp(); + //remove all VPN related entries + floatingIpListener.createNATFlowEntries(dpnId, portName, routerId.getValue(), network.getId(), ipMap.getInternalIp(), externalIp); + } + } + } + + // SNAT + for(Uuid routerId : routerIds) { + LOG.debug("NAT Service : associateExternalNetworkWithVPN() for routerId {}", routerId); + Uuid networkId = network.getId(); + if(networkId == null) { + LOG.error("NAT Service : networkId is null for the router ID {}", routerId); + return; + } + final String vpnName = network.getVpnid().getValue(); + if(vpnName == null) { + LOG.error("NAT Service : No VPN associated with ext nw {} for router {}", networkId, routerId); + return; + } + + BigInteger dpnId = new BigInteger("0"); + InstanceIdentifier routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerId.getValue()); + Optional rtrToNapt = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch ); + if(rtrToNapt.isPresent()) { + dpnId = rtrToNapt.get().getPrimarySwitchId(); + } + LOG.debug("NAT Service : got primarySwitch as dpnId{} ", dpnId); + + Long routerIdentifier = NatUtil.getVpnId(dataBroker, routerId.getValue()); + InstanceIdentifierBuilder idBuilder = + InstanceIdentifier.builder(IntextIpMap.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping.class, new org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey(routerIdentifier)); + InstanceIdentifier id = idBuilder.build(); + Optional ipMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id); + if (ipMapping.isPresent()) { + List ipMaps = ipMapping.get().getIpMap(); + for (IpMap ipMap : ipMaps) { + String externalIp = ipMap.getExternalIp(); + LOG.debug("NAT Service : got externalIp as {}", externalIp); + LOG.debug("NAT Service : About to call advToBgpAndInstallFibAndTsFlows for dpnId {}, vpnName {} and externalIp {}", dpnId, vpnName, externalIp); + externalRouterListener.advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, NatUtil.getVpnId(dataBroker, routerId.getValue()), externalIp, vpnService, fibService, bgpManager, dataBroker, LOG); + } + } else { + LOG.warn("NAT Service : No ipMapping present fot the routerId {}", routerId); + } + + long vpnId = NatUtil.getVpnId(dataBroker, vpnName); + // Install 47 entry to point to 21 + if(vpnId != -1) { + LOG.debug("NAT Service : Calling externalRouterListener installNaptPfibEntry for donId {} and vpnId {}", dpnId, vpnId); + externalRouterListener.installNaptPfibEntry(dpnId, vpnId); + } + + } + + } + + private void disassociateExternalNetworkFromVPN(Networks network, String vpnName) { + List routerIds = network.getRouterIds(); + + //long vpnId = NatUtil.getVpnId(dataBroker, vpnName); + for(Uuid routerId : routerIds) { + //long router = NatUtil.getVpnId(dataBroker, routerId.getValue()); + + InstanceIdentifier routerPortsId = NatUtil.getRouterPortsId(routerId.getValue()); + Optional optRouterPorts = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsId); + if(!optRouterPorts.isPresent()) { + LOG.debug("Could not read Router Ports data object with id: {} to handle disassociate ext nw {}", routerId, network.getId()); + continue; + } + RouterPorts routerPorts = optRouterPorts.get(); + List interfaces = routerPorts.getPorts(); + for(Ports port : interfaces) { + String portName = port.getPortName(); + BigInteger dpnId = getDpnForInterface(interfaceManager, portName); + if(dpnId.equals(BigInteger.ZERO)) { + LOG.debug("DPN not found for {}, skip handling of ext nw {} disassociation", portName, network.getId()); + continue; + } + List ipMapping = port.getIpMapping(); + for(IpMapping ipMap : ipMapping) { + String externalIp = ipMap.getExternalIp(); + floatingIpListener.removeNATFlowEntries(dpnId, portName, vpnName, routerId.getValue(), network.getId(), ipMap.getInternalIp(), externalIp); + } + } + } + } + + public static BigInteger getDpnForInterface(OdlInterfaceRpcService interfaceManagerRpcService, String ifName) { + BigInteger nodeId = BigInteger.ZERO; + try { + GetDpidFromInterfaceInput + dpIdInput = + new GetDpidFromInterfaceInputBuilder().setIntfName(ifName).build(); + Future> + dpIdOutput = + interfaceManagerRpcService.getDpidFromInterface(dpIdInput); + RpcResult dpIdResult = dpIdOutput.get(); + if (dpIdResult.isSuccessful()) { + nodeId = dpIdResult.getResult().getDpid(); + } else { + LOG.error("Could not retrieve DPN Id for interface {}", ifName); + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("Exception when getting dpn for interface {}", ifName, e); + } + return nodeId; + } + +} 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 new file mode 100644 index 00000000..3e6db2d0 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java @@ -0,0 +1,1130 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +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.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; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.datastoreutils.AsyncDataTreeChangeListenerBase; +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +import org.opendaylight.vpnservice.mdsalutil.BucketInfo; +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.GroupEntity; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInputBuilder; +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.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; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIds; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.Subnetmaps; +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; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.JdkFutureAdapters; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Created by EYUGSAR on 2/20/2016. + */ + +public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase{ + + private static final Logger LOG = LoggerFactory.getLogger( ExternalRoutersListener.class); + private static long label; + private ListenerRegistration listenerRegistration; + private final DataBroker dataBroker; + private IMdsalApiManager mdsalManager; + private ItmRpcService itmManager; + private OdlInterfaceRpcService interfaceManager; + private IdManagerService idManager; + private NaptManager naptManager; + private NAPTSwitchSelector naptSwitchSelector; + private IBgpManager bgpManager; + private VpnRpcService vpnService; + private FibRpcService fibService; + private SNATDefaultRouteProgrammer defaultRouteProgrammer; + private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16); + static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000022", 16); + + public void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + public void setItmManager(ItmRpcService itmManager) { + this.itmManager = itmManager; + } + + public void setIdManager(IdManagerService idManager) { + this.idManager = idManager; + createGroupIdPool(); + } + + void setDefaultProgrammer(SNATDefaultRouteProgrammer defaultRouteProgrammer) { + this.defaultRouteProgrammer = defaultRouteProgrammer; + } + + + public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) { + this.interfaceManager = interfaceManager; + } + + public void setNaptManager(NaptManager naptManager) { + this.naptManager = naptManager; + } + + public void setNaptSwitchSelector(NAPTSwitchSelector naptSwitchSelector) { + this.naptSwitchSelector = naptSwitchSelector; + } + + public void setBgpManager(IBgpManager bgpManager) { + this.bgpManager = bgpManager; + } + + public void setVpnService(VpnRpcService vpnService) { + this.vpnService = vpnService; + } + + public void setFibService(FibRpcService fibService) { + this.fibService = fibService; + } + + public ExternalRoutersListener(DataBroker dataBroker ) + { + super( Routers.class, ExternalRoutersListener.class ); + this.dataBroker = dataBroker; + } + + @Override + protected void add(InstanceIdentifier identifier, Routers routers) { + + LOG.info( "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); + + if( !routers.isEnableSnat()) { + LOG.info( "SNAT is disabled for external router {} ", routers.getRouterName()); + return; + } + + // Populate the router-id-name container + String routerName = routers.getRouterName(); + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + RouterIds rtrs = new RouterIdsBuilder().setKey(new RouterIdsKey(routerId)).setRouterId(routerId).setRouterName(routerName).build(); + MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.CONFIGURATION, getRoutersIdentifier(routerId), rtrs); + + handleEnableSnat(routerName); + } + + public void handleEnableSnat(String routerName){ + LOG.info("Handling SNAT for router {}", routerName); + + // Allocate Primary Napt Switch for this router + BigInteger primarySwitchId = naptSwitchSelector.selectNewNAPTSwitch(routerName); + + 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 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); + } + } + + // 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; + List externalIps = null; + + InstanceIdentifier id = InstanceIdentifier + .builder(ExtRouters.class) + .child(Routers.class, new RoutersKey(routerName)) + .build(); + + Optional extRouters = read(dataBroker, LogicalDatastoreType.CONFIGURATION, id); + + if(extRouters.isPresent()) + { + LOG.debug("NAT Service : Fetching values from extRouters model"); + Routers routerEntry= extRouters.get(); + subnetList = routerEntry.getSubnetIds(); + externalIps = routerEntry.getExternalIps(); + int counter = 0; + int extIpCounter = externalIps.size(); + LOG.debug("NAT Service : counter values before looping counter {} and extIpCounter {}", counter, extIpCounter); + for(Uuid subnet : subnetList) { + LOG.debug("NAT Service : Looping internal subnets for subnet {}", subnet); + InstanceIdentifier subnetmapId = InstanceIdentifier + .builder(Subnetmaps.class) + .child(Subnetmap.class, new SubnetmapKey(subnet)) + .build(); + Optional sn = read(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetmapId); + if(sn.isPresent()){ + // subnets + Subnetmap subnetmapEntry = sn.get(); + String subnetString = subnetmapEntry.getSubnetIp(); + String[] subnetSplit = subnetString.split("/"); + String subnetIp = subnetSplit[0]; + String subnetPrefix = "0"; + if(subnetSplit.length == 2) { + subnetPrefix = subnetSplit[1]; + } + IPAddress subnetAddr = new IPAddress(subnetIp, Integer.parseInt(subnetPrefix)); + LOG.debug("NAT Service : subnetAddr is {} and subnetPrefix is {}", subnetAddr.getIpAddress(), subnetAddr.getPrefixLength()); + //externalIps + LOG.debug("NAT Service : counter values counter {} and extIpCounter {}", counter, extIpCounter); + if(extIpCounter != 0) { + if(counter < extIpCounter) { + String[] IpSplit = externalIps.get(counter).split("/"); + String externalIp = IpSplit[0]; + String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX); + if(IpSplit.length==2) { + extPrefix = IpSplit[1]; + } + IPAddress externalIpAddr = new IPAddress(externalIp, Integer.parseInt(extPrefix)); + LOG.debug("NAT Service : externalIp is {} and extPrefix is {}", externalIpAddr.getIpAddress(), externalIpAddr.getPrefixLength()); + naptManager.registerMapping(segmentId, subnetAddr, externalIpAddr); + LOG.debug("NAT Service : Called registerMapping for subnetIp {}, prefix {}, externalIp {}. prefix {}", subnetIp, subnetPrefix, + externalIp, extPrefix); + + String externalIpAddrPrefix = externalIpAddr.getIpAddress() + "/" + externalIpAddr.getPrefixLength(); + LOG.debug("NAT Service : Calling handleSnatReverseTraffic for primarySwitchId {}, routerName {} and externalIpAddPrefix {}", primarySwitchId, routerName, externalIpAddrPrefix); + handleSnatReverseTraffic(primarySwitchId, segmentId, externalIpAddrPrefix); + + } else { + counter = 0; //Reset the counter which runs on externalIps for round-robbin effect + LOG.debug("NAT Service : Counter on externalIps got reset"); + String[] IpSplit = externalIps.get(counter).split("/"); + String externalIp = IpSplit[0]; + String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX); + if(IpSplit.length==2) { + extPrefix = IpSplit[1]; + } + IPAddress externalIpAddr = new IPAddress(externalIp, Integer.parseInt(extPrefix)); + LOG.debug("NAT Service : externalIp is {} and extPrefix is {}", externalIpAddr.getIpAddress(), externalIpAddr.getPrefixLength()); + naptManager.registerMapping(segmentId, subnetAddr, externalIpAddr); + LOG.debug("NAT Service : Called registerMapping for subnetIp {}, prefix {}, externalIp {}. prefix {}", subnetIp, subnetPrefix, + externalIp, extPrefix); + + String externalIpAddrPrefix = externalIpAddr.getIpAddress() + "/" + externalIpAddr.getPrefixLength(); + LOG.debug("NAT Service : Calling handleSnatReverseTraffic for primarySwitchId {}, routerName {} and externalIpAddPrefix {}", primarySwitchId, routerName, externalIpAddrPrefix); + handleSnatReverseTraffic(primarySwitchId, segmentId, externalIpAddrPrefix); + + } + } + counter++; + LOG.debug("NAT Service : Counter on externalIps incremented to {}", counter); + + } else { + LOG.warn("NAT Service : No internal subnets present in extRouters Model"); + } + } + } + + LOG.info("NAT Service : handleEnableSnat() Exit"); + } + + private void addOrDelDefFibRouteToSNAT(String routerName, boolean create) { + //Router ID is used as the internal VPN's name, hence the vrf-id in VpnInstance Op DataStore + 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; + } + 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); + return; + } + 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); + } + } + } + } + + public static Optional read(DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier path) + { + ReadOnlyTransaction tx = broker.newReadOnlyTransaction(); + + Optional result = Optional.absent(); + try + { + result = tx.read(datastoreType, path).get(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return result; + } + + public void close() throws Exception + { + if (listenerRegistration != null) + { + try + { + listenerRegistration.close(); + } + catch (final Exception e) + { + LOG.error("Error when cleaning up ExternalRoutersListener.", e); + } + + listenerRegistration = null; + } + LOG.debug("ExternalRoutersListener Closed"); + } + + protected void installOutboundMissEntry(String routerName, BigInteger primarySwitchId) { + long routerId = NatUtil.getVpnId(dataBroker, routerName); + LOG.debug("NAT Service : Router ID from getVpnId {}", routerId); + if(routerId != NatConstants.INVALID_ID) { + LOG.debug("NAT Service : Creating miss entry on primary {}, for router {}", primarySwitchId, routerId); + createOutboundTblEntry(primarySwitchId, routerId); + } else { + LOG.error("NAT Service : Unable to fetch Router Id for RouterName {}, failed to createAndInstallMissEntry", routerName); + } + } + + public String getFlowRefOutbound(BigInteger dpnId, short tableId, long routerID) { + return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR). + append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString(); + } + + public BigInteger getCookieOutboundFlow(long routerId) { + return NatConstants.COOKIE_OUTBOUND_NAPT_TABLE.add(new BigInteger("0110001", 16)).add( + BigInteger.valueOf(routerId)); + } + + protected FlowEntity buildOutboundFlowEntity(BigInteger dpId, long routerId) { + LOG.debug("NAT Service : buildOutboundFlowEntity called for dpId {} and routerId{}", dpId, routerId); + 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 })); + + 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(routerId), 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 createOutboundTblEntry(BigInteger dpnId, long routerId) { + LOG.debug("NAT Service : createOutboundTblEntry called for dpId {} and routerId {}", dpnId, routerId); + FlowEntity flowEntity = buildOutboundFlowEntity(dpnId, routerId); + LOG.debug("NAT Service : Installing flow {}", flowEntity); + mdsalManager.installFlow(flowEntity); + } + + protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) { + try { + Future> result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder() + .setSourceDpid(srcDpId) + .setDestinationDpid(dstDpId).build()); + RpcResult rpcResult = result.get(); + if(!rpcResult.isSuccessful()) { + LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors()); + } else { + return rpcResult.getResult().getInterfaceName(); + } + } catch (InterruptedException | ExecutionException | NullPointerException e) { + LOG.warn("NAT Service : Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId); + } + + return null; + } + + protected List getEgressActionsForInterface(String ifName, long routerId) { + LOG.debug("NAT Service : getEgressActionsForInterface called for interface {}", ifName); + List listActionInfo = new ArrayList(); + try { + Future> result = + interfaceManager.getEgressActionsForInterface( + new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build()); + RpcResult rpcResult = result.get(); + if(!rpcResult.isSuccessful()) { + LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", ifName, rpcResult.getErrors()); + } else { + List actions = + rpcResult.getResult().getAction(); + for (Action action : actions) { + org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction(); + if (actionClass instanceof OutputActionCase) { + listActionInfo.add(new ActionInfo(ActionType.output, + new String[] {((OutputActionCase)actionClass).getOutputAction() + .getOutputNodeConnector().getValue()})); + } else if (actionClass instanceof PushVlanActionCase) { + listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {})); + } else if (actionClass instanceof SetFieldCase) { + if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) { + int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch().getVlanId().getVlanId().getValue(); + listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid, + new String[] { Long.toString(vlanVid) })); + } + } + } + } + } catch (InterruptedException | ExecutionException e) { + LOG.warn("Exception when egress actions for interface {}", ifName, e); + } + return listActionInfo; + } + + protected void installSnatMissEntry(BigInteger dpnId, List bucketInfo, String routerName) { + LOG.debug("NAT Service : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId, bucketInfo.get(0)); + // Install the select group + 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); + // Install miss entry pointing to group + FlowEntity flowEntity = buildSnatFlowEntity(dpnId, routerName, groupId); + mdsalManager.installFlow(flowEntity); + } + + public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId) { + + LOG.debug("NAT Service : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}", dpId, routerName, groupId ); + long routerId = NatUtil.getVpnId(dataBroker, routerName); + 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 })); + + + List instructions = new ArrayList(); + List actionsInfo = new ArrayList(); + + ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] { + BigInteger.valueOf(routerId)}) ; + 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 installTerminatingServiceTblEntry(BigInteger dpnId, String routerName) { + LOG.debug("NAT Service : creating entry for Terminating Service Table for switch {}, routerName {}", dpnId, routerName); + FlowEntity flowEntity = buildTsFlowEntity(dpnId, routerName); + mdsalManager.installFlow(flowEntity); + + } + + private FlowEntity buildTsFlowEntity(BigInteger dpId, String routerName) { + + BigInteger routerId = BigInteger.valueOf (NatUtil.getVpnId(dataBroker, routerName)); + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + matches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {routerId })); + + List instructions = new ArrayList(); + instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] + { routerId, 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 String getFlowRefTs(BigInteger dpnId, short tableId, long routerID) { + return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR). + append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString(); + } + + public static String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) { + return new StringBuilder().append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR). + append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString(); + } + + private String getGroupIdKey(String routerName){ + String groupIdKey = new String("snatmiss." + routerName); + return groupIdKey; + } + + protected long createGroupId(String groupIdKey) { + AllocateIdInput getIdInput = new AllocateIdInputBuilder() + .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey) + .build(); + try { + Future> result = idManager.allocateId(getIdInput); + RpcResult rpcResult = result.get(); + return rpcResult.getResult().getIdValue(); + } catch (NullPointerException | InterruptedException | ExecutionException e) { + LOG.trace("",e); + } + return 0; + } + + protected void createGroupIdPool() { + CreateIdPoolInput createPool = new CreateIdPoolInputBuilder() + .setPoolName(NatConstants.SNAT_IDPOOL_NAME) + .setLow(NatConstants.SNAT_ID_LOW_VALUE) + .setHigh(NatConstants.SNAT_ID_HIGH_VALUE) + .build(); + try { + Future> result = idManager.createIdPool(createPool); + if ((result != null) && (result.get().isSuccessful())) { + LOG.debug("NAT Service : Created GroupIdPool"); + } else { + LOG.error("NAT Service : Unable to create GroupIdPool"); + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("Failed to create PortPool for NAPT Service",e); + } + } + + protected void handleSwitches (BigInteger dpnId, String routerName, BigInteger primarySwitchId) { + LOG.debug("NAT Service : Installing SNAT miss entry in switch {}", dpnId); + List listActionInfoPrimary = new ArrayList<>(); + String ifNamePrimary = getTunnelInterfaceName( dpnId, 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); + installSnatMissEntry(dpnId, listBucketInfo, routerName); + + } + + protected void handlePrimaryNaptSwitch (BigInteger dpnId, String routerName, BigInteger primarySwitchId) { + + /* + * Primary NAPT Switch – bucket Should always point back to its own Outbound Table + */ + + LOG.debug("NAT Service : Installing SNAT miss entry in Primary NAPT switch {} ", dpnId); + + 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); + + long routerId = NatUtil.getVpnId(dataBroker, routerName); + + installSnatMissEntry(dpnId, listBucketInfo, routerName); + installTerminatingServiceTblEntry(dpnId, routerName); + installNaptPfibEntry(dpnId, routerId); + + } + + 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 FlowEntity buildNaptPfibFlowEntity(BigInteger dpId, long routerId) { + + LOG.debug("NAT Service : buildNaptPfibFlowEntity is called for dpId {}, routerId {}", dpId, routerId ); + 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 })); + + 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); + 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; + } + + private void handleSnatReverseTraffic(BigInteger dpnId, long routerId, String externalIp) { + LOG.debug("NAT Service : handleSnatReverseTraffic() entry for DPN ID, routerId, externalIp : {}", dpnId, routerId, externalIp); + Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId); + if(networkId == null) { + LOG.error("NAT Service : networkId is null for the router ID {}", routerId); + return; + } + final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG); + if(vpnName == null) { + LOG.error("NAT Service : No VPN associated with ext nw {} to handle add external ip configuration {} in router {}", + networkId, externalIp, routerId); + return; + } + advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG); + LOG.debug("NAT Service : handleSnatReverseTraffic() exit for DPN ID, routerId, externalIp : {}", dpnId, routerId, externalIp); + } + + public void advToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId, final String vpnName, final long routerId, final String externalIp, + VpnRpcService vpnService, final FibRpcService fibService, final IBgpManager bgpManager, final DataBroker dataBroker, + final Logger log){ + LOG.debug("NAT Service : advToBgpAndInstallFibAndTsFlows() entry for DPN ID {}, tableId {}, vpnname {} and externalIp {}", dpnId, tableId, vpnName, externalIp); + //Generate VPN label for the external IP + GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build(); + Future> labelFuture = vpnService.generateVpnLabel(labelInput); + + //On successful generation of the VPN label, advertise the route to the BGP and install the FIB routes. + ListenableFuture> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction, RpcResult>() { + + @Override + public ListenableFuture> apply(RpcResult result) throws Exception { + if (result.isSuccessful()) { + LOG.debug("NAT Service : inside apply with result success"); + GenerateVpnLabelOutput output = result.getResult(); + long label = output.getLabel(); + + //Inform BGP + String rd = NatUtil.getVpnRd(dataBroker, vpnName); + String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId); + NatUtil.addPrefixToBGP(bgpManager, rd, externalIp, nextHopIp, label, log); + + //Get IPMaps from the DB for the router ID + List 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); + } + } + + //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); + + CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId) + .setIpAddress(externalIp).setServiceId(label).setInstruction(customInstructions).build(); + Future> future = fibService.createFibEntry(input); + return JdkFutureAdapters.listenInPoolThread(future); + } else { + LOG.error("NAT Service : inside apply with result failed"); + String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors()); + return Futures.immediateFailedFuture(new RuntimeException(errMsg)); + } + } + }); + + Futures.addCallback(future, new FutureCallback>() { + + @Override + public void onFailure(Throwable error) { + log.error("NAT Service : Error in generate label or fib install process", error); + } + + @Override + public void onSuccess(RpcResult result) { + if (result.isSuccessful()) { + log.info("NAT Service : Successfully installed custom FIB routes for prefix {}", externalIp); + } else { + log.error("NAT Service : Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors()); + } + } + }); + } + + private void makeLFibTableEntry(BigInteger dpId, long serviceId, List customInstructions) { + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + 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); + instructions.add(writeInstruction); + instructions.addAll(customInstructions); + + // Install the flow entry in L3_LFIB_TABLE + String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, ""); + + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef, + 10, flowRef, 0, 0, + COOKIE_VM_LFIB_TABLE, matches, instructions); + + mdsalManager.installFlow(dpId, flowEntity); + + LOG.debug("NAT Service : LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId ); + } + + private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List customInstructions) { + List mkMatches = new ArrayList(); + + LOG.debug("NAT Service : Create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId); + + 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); + + mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity); + } + + protected InstanceIdentifier getRoutersIdentifier(long routerId) { + InstanceIdentifier id = InstanceIdentifier.builder( + RouterIdName.class).child(RouterIds.class, new RouterIdsKey(routerId)).build(); + return id; + } + + private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) { + return new StringBuilder(64).append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR) + .append(tableId).append(NwConstants.FLOWID_SEPARATOR) + .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString(); + } + + @Override + protected void update(InstanceIdentifier identifier, Routers original, Routers update) { + boolean originalSNATEnabled = original.isEnableSnat(); + boolean updatedSNATEnabled = update.isEnableSnat(); + 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); + } else { + String routerName = original.getRouterName(); + LOG.info("NAT Service : SNAT enabled for Router {}", original.getRouterName()); + handleEnableSnat(routerName); + } + } + } + + @Override + protected void remove(InstanceIdentifier identifier, Routers router) { + LOG.trace("NAT Service : Router delete method"); + { + /* + ROUTER DELETE SCENARIO + 1) Get the router ID from the event. + 2) Build the cookie information from the router ID. + 3) Get the primary and secondary switch DPN IDs using the router ID from the model. + 4) Build the flow with the cookie value. + 5) Delete the flows which matches the cookie information from the NAPT outbound, inbound tables. + 6) Remove the flows from the other switches which points to the primary and secondary switches for the flows related the router ID. + 7) Get the list of external IP address maintained for the router ID. + 8) Use the NaptMananager removeMapping API to remove the list of IP addresses maintained. + 9) Withdraw the corresponding routes from the BGP. + */ + + if (identifier == null || router == null) { + LOG.info("++++++++++++++NAT Service : ExternalRoutersListener:remove:: returning without processing since routers is null"); + return; + } + + String routerName = router.getRouterName(); + LOG.info("Removing default NAT route from FIB on all dpns part of router {} ", routerName); + addOrDelDefFibRouteToSNAT(routerName, false); + Uuid networkUuid = router.getNetworkId(); + List externalIps = router.getExternalIps(); + handleDisableSnat(routerName, networkUuid, externalIps); + } + } + + public void handleDisableSnat(String routerName, Uuid networkUuid, List externalIps){ + LOG.info("NAT Service : handleDisableSnat() Entry"); + 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); + + 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); + + LOG.info("NAT Service : handleDisableSnat() Exit"); + } + + public void removeNaptFlowsFromActiveSwitch(long routerId, String routerName, BigInteger dpnId){ + LOG.debug("NAT Service : Remove NAPT flows from Active switch"); + BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId); + + //Remove the PSNAT entry which forwards the packet to Terminating Service table + String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, routerName); + FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef); + + LOG.info("NAT Service : Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId); + mdsalManager.removeFlow(pSNatFlowEntity); + + //Remove the group entry which resubmits the packet to the Terminating Service table or to the out port accordingly. + long groupId = createGroupId(getGroupIdKey(routerName)); + List listBucketInfo = new ArrayList(); + GroupEntity pSNatGroupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, listBucketInfo); + + LOG.info("NAT Service : Remove the group {} for the active switch with the DPN ID {} and router ID {}", groupId, dpnId, routerId); + mdsalManager.removeGroup(pSNatGroupEntity); + + //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table + String tsFlowRef = getFlowRefTs(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, routerId); + FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef); + + LOG.info("NAT Service : Remove the flow in the " + NatConstants.TERMINATING_SERVICE_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId); + mdsalManager.removeFlow(tsNatFlowEntity); + + //Remove the Outbound flow entry which forwards the packet to FIB Table + String outboundNatFlowRef = getFlowRefOutbound(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, routerId); + FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef); + + 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 + 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); + + //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]; + + //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); + } + } + } + + public void removeFlowsFromNonActiveSwitches(String routerName, BigInteger naptSwitchDpnId){ + 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); + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + for (VpnToDpnList eachSwitch : allSwitchList) { + BigInteger dpnId = eachSwitch.getDpnId(); + if (naptSwitchDpnId != dpnId) { + LOG.info("NAT Service : Handle Ordinary switch"); + + //Remove the PSNAT entry which forwards the packet to Terminating Service table + String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, String.valueOf(routerName)); + FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef); + + LOG.info("Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the non active switch with the DPN ID {} and router ID {}", dpnId, routerId); + mdsalManager.removeFlow(pSNatFlowEntity); + + //Remove the group entry which resubmits the packet to the Terminating Service table or to the out port accordingly. + long groupId = createGroupId(getGroupIdKey(routerName)); + List listBucketInfo = new ArrayList(); + GroupEntity pSNatGroupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, listBucketInfo); + + LOG.info("NAT Service : Remove the group {} for the non active switch with the DPN ID {} and router ID {}", groupId, dpnId, routerId); + mdsalManager.removeGroup(pSNatGroupEntity); + + } + } + } + + public void advToBgpAndRemoveFibAndTsFlows(final BigInteger dpnId, Long routerId, Uuid networkUuid, List externalIps){ + //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"); + 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); + return; + } + + //Inform BGP about the route removal + String rd = NatUtil.getVpnRd(dataBroker, vpnName); + String prefix = "32"; + NatUtil.removePrefixFromBGP(bgpManager, rd, prefix, LOG); + + //Remove custom FIB routes + //Future> removeFibEntry(RemoveFibEntryInput input); + final String externalIp = externalIps.get(0); + + //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"); + return; + } + + long tempLabel = -1; + 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)) { + tempLabel = dbIpMap.getLabel(); + break; + } + } + if(tempLabel == -1){ + LOG.error("NAT Service : Label is null"); + return; + } + + final long label = tempLabel; + RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/" + + NatConstants.DEFAULT_PREFIX).setServiceId(label).build(); + Future> future = fibService.removeFibEntry(input); + + ListenableFuture> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction, RpcResult>() { + + @Override + public ListenableFuture> apply(RpcResult result) throws Exception { + //Release label + if (result.isSuccessful()) { + removeTunnelTableEntry(dpnId, label); + removeLFibTableEntry(dpnId, label); + RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build(); + Future> labelFuture = vpnService.removeVpnLabel(labelInput); + return JdkFutureAdapters.listenInPoolThread(labelFuture); + } else { + String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors()); + LOG.error(errMsg); + return Futures.immediateFailedFuture(new RuntimeException(errMsg)); + } + } + + }); + + Futures.addCallback(labelFuture, new FutureCallback>() { + + @Override + public void onFailure(Throwable error) { + LOG.error("NAT Service : Error in removing the label or custom fib entries", error); + } + + @Override + public void onSuccess(RpcResult result) { + if (result.isSuccessful()) { + LOG.debug("NAT Service : Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName); + } else { + LOG.error("NAT Service : Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors()); + } + } + }); + } + + private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) { + LOG.info("NAT Service : remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId); + List mkMatches = new ArrayList(); + // Matching metadata + mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)})); + Flow flowEntity = 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, null); + mdsalManager.removeFlow(dpnId, flowEntity); + LOG.debug("NAT Service : Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId); + } + + private void removeLFibTableEntry(BigInteger dpnId, long serviceId) { + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x8847L })); + matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)})); + + String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, ""); + + LOG.debug("NAT Service : removing LFib entry with flow ref {}", flowRef); + + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef, + 10, flowRef, 0, 0, + COOKIE_VM_LFIB_TABLE, matches, null); + + mdsalManager.removeFlow(dpnId, flowEntity); + + 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 getWildCardPath() + { + return InstanceIdentifier.create(ExtRouters.class).child(Routers.class); + } + + @Override + protected ExternalRoutersListener getDataTreeChangeListener() + { + return ExternalRoutersListener.this; + } + +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPHandler.java new file mode 100644 index 00000000..8b1ad65a --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPHandler.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; + +public interface FloatingIPHandler { + + void onAddFloatingIp(BigInteger dpnId, String routerId, Uuid networkId, String interfaceName, String externalIp, + String internalIp); + + void onRemoveFloatingIp(BigInteger dpnId, String routerId, Uuid networkId, String externalIp, String internalIp, + long label); + +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPListener.java new file mode 100644 index 00000000..7b43c594 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPListener.java @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +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.FloatingIpInfo; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMappingBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.NetworksKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yangtools.yang.common.RpcResult; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import com.google.common.base.Optional; +import com.google.common.base.Strings; + +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by emhamla on 1/18/2016. + */ +public class FloatingIPListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener implements AutoCloseable{ + private static final Logger LOG = LoggerFactory.getLogger(FloatingIPListener.class); + private ListenerRegistration listenerRegistration; + private final DataBroker broker; + private OdlInterfaceRpcService interfaceManager; + private IMdsalApiManager mdsalManager; + private FloatingIPHandler handler; + + + public FloatingIPListener (final DataBroker db) { + super(IpMapping.class); + broker = db; + registerListener(db); + } + + void setFloatingIpHandler(FloatingIPHandler handler) { + this.handler = handler; + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + LOG.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + LOG.info("FloatingIP Listener Closed"); + } + + private void registerListener(final DataBroker db) { + try { + listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, + getWildCardPath(), FloatingIPListener.this, AsyncDataBroker.DataChangeScope.SUBTREE); + } catch (final Exception e) { + LOG.error("FloatingIP DataChange listener registration fail!", e); + throw new IllegalStateException("FloatingIP Listener registration Listener failed.", e); + } + } + + private InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(FloatingIpInfo.class).child(RouterPorts.class).child(Ports.class).child(IpMapping.class); + } + + public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) { + this.interfaceManager = interfaceManager; + } + + public void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + @Override + protected void add(final InstanceIdentifier identifier, + final IpMapping mapping) { + LOG.trace("FloatingIPListener add ip mapping method - key: " + identifier + ", value=" + mapping ); + processFloatingIPAdd(identifier, mapping); + } + + @Override + protected void remove(InstanceIdentifier identifier, IpMapping mapping) { + LOG.trace("FloatingIPListener remove ip mapping method - key: " + identifier + ", value=" + mapping ); + processFloatingIPDel(identifier, mapping); + } + + @Override + protected void update(InstanceIdentifier identifier, IpMapping original, IpMapping update) { + LOG.trace("FloatingIPListener update ip mapping method - key: " + identifier + ", original=" + original + ", update=" + update ); + } + + public static BigInteger getDpnForInterface(OdlInterfaceRpcService interfaceManagerRpcService, String ifName) { + BigInteger nodeId = BigInteger.ZERO; + try { + GetDpidFromInterfaceInput + dpIdInput = + new GetDpidFromInterfaceInputBuilder().setIntfName(ifName).build(); + Future> + dpIdOutput = + interfaceManagerRpcService.getDpidFromInterface(dpIdInput); + RpcResult dpIdResult = dpIdOutput.get(); + if (dpIdResult.isSuccessful()) { + nodeId = dpIdResult.getResult().getDpid(); + } else { + LOG.error("Could not retrieve DPN Id for interface {}", ifName); + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("Exception when getting dpn for interface {}", ifName, e); + } + return nodeId; + } + + private FlowEntity buildPreDNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId, long vpnId) { + + LOG.info("Bulding DNAT Flow entity for ip {} ", externalIp); + + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] { + externalIp, "32" })); + +// matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { +// BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + + List actionsInfos = new ArrayList(); + actionsInfos.add(new ActionInfo(ActionType.set_destination_ip, new String[]{ internalIp, "32" })); + + List instructions = new ArrayList(); + instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf + (routerId), MetaDataUtil.METADATA_MASK_VRFID })); + instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos)); + instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.DNAT_TABLE })); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PDNAT_TABLE, externalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PDNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, matches, instructions); + + return flowEntity; + } + + + + private FlowEntity buildDNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId) { + + LOG.info("Bulding DNAT Flow entity for ip {} ", externalIp); + + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { + BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID })); + + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] { + // externalIp, "32" })); + internalIp, "32" })); + + List actionsInfos = new ArrayList(); +// actionsInfos.add(new ActionInfo(ActionType.set_destination_ip, new String[]{ internalIp, "32" })); + + List instructions = new ArrayList(); +// instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf +// (routerId), MetaDataUtil.METADATA_MASK_VRFID })); + actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) })); + instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos)); + //instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.L3_FIB_TABLE })); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.DNAT_TABLE, externalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.DNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, matches, instructions); + + return flowEntity; + + } + + private FlowEntity buildPreSNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long vpnId, long routerId) { + + LOG.info("Building PSNAT Flow entity for ip {} ", internalIp); + + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + matches.add(new MatchInfo(MatchFieldType.ipv4_source, new String[] { + internalIp, "32" })); + + matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { + BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID })); + + List actionsInfos = new ArrayList(); + actionsInfos.add(new ActionInfo(ActionType.set_source_ip, new String[]{ externalIp, "32" })); + + List instructions = new ArrayList(); + instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos)); + instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.SNAT_TABLE })); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PSNAT_TABLE, internalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, matches, instructions); + + return flowEntity; + } + + private FlowEntity buildSNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long vpnId, String macAddress) { + + LOG.info("Building SNAT Flow entity for ip {} ", internalIp); + + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { + BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + matches.add(new MatchInfo(MatchFieldType.ipv4_source, new String[] { + // internalIp, "32" })); + externalIp, "32" })); + + List actionsInfos = new ArrayList(); +// actionsInfos.add(new ActionInfo(ActionType.set_source_ip, new String[]{ externalIp, "32" })); + + //TODO: Set external gateway mac address + if(!Strings.isNullOrEmpty(macAddress)) { + LOG.debug("Setting ext gw mac address {} in SNAT {} flow action", macAddress, internalIp); + actionsInfos.add(new ActionInfo(ActionType.set_field_eth_dest, new String[]{ macAddress })); + } + + List instructions = new ArrayList(); + //instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) })); + instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos)); + //instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.L3_FIB_TABLE })); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.SNAT_TABLE, internalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.SNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, matches, instructions); + + return flowEntity; + + + } + + private void createDNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long routerId, long vpnId) { + FlowEntity pFlowEntity = buildPreDNATFlowEntity(dpnId, internalIp, externalIp, routerId, vpnId ); + mdsalManager.installFlow(pFlowEntity); + + FlowEntity flowEntity = buildDNATFlowEntity(dpnId, internalIp, externalIp, routerId); + mdsalManager.installFlow(flowEntity); + } + + private void removeDNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long routerId) { + FlowEntity pFlowEntity = buildPreDNATDeleteFlowEntity(dpnId, internalIp, externalIp, routerId ); + mdsalManager.removeFlow(pFlowEntity); + + FlowEntity flowEntity = buildDNATDeleteFlowEntity(dpnId, internalIp, externalIp, routerId); + mdsalManager.removeFlow(flowEntity); + } + + private void createSNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long vpnId, long routerId, String macAddress) { + FlowEntity pFlowEntity = buildPreSNATFlowEntity(dpnId, internalIp, externalIp, vpnId , routerId); + mdsalManager.installFlow(pFlowEntity); + + FlowEntity flowEntity = buildSNATFlowEntity(dpnId, internalIp, externalIp, vpnId, macAddress); + mdsalManager.installFlow(flowEntity); + + } + + private void removeSNATTblEntry(BigInteger dpnId, String internalIp, String externalIp) { + FlowEntity pFlowEntity = buildPreSNATDeleteFlowEntity(dpnId, internalIp, externalIp); + mdsalManager.removeFlow(pFlowEntity); + + FlowEntity flowEntity = buildSNATDeleteFlowEntity(dpnId, internalIp, externalIp); + mdsalManager.removeFlow(flowEntity); + + } + + private Uuid getExtNetworkId(final InstanceIdentifier pIdentifier) { + Optional rtrPort = NatUtil.read(broker, LogicalDatastoreType.CONFIGURATION, pIdentifier); + if(!rtrPort.isPresent()) { + LOG.error("Unable to read router port entry for {}", pIdentifier); + return null; + } + + Uuid extNwId = rtrPort.get().getExternalNetworkId(); + return extNwId; + } + + private long getVpnId(Uuid extNwId) { + InstanceIdentifier nwId = InstanceIdentifier.builder(ExternalNetworks.class).child(Networks.class, new NetworksKey(extNwId)).build(); + Optional nw = NatUtil.read(broker, LogicalDatastoreType.CONFIGURATION, nwId); + if(!nw.isPresent()) { + LOG.error("Unable to read external network for {}", extNwId); + return NatConstants.INVALID_ID; + } + + Uuid vpnUuid = nw.get().getVpnid(); + if(vpnUuid == null) { + return NatConstants.INVALID_ID; + } + + //Get the id using the VPN UUID (also vpn instance name) + return NatUtil.readVpnId(broker, vpnUuid.getValue()); + + } + + private void processFloatingIPAdd(final InstanceIdentifier identifier, + final IpMapping mapping) { + LOG.trace("Add event - key: {}, value: {}", identifier, mapping); + + final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId(); + final PortsKey pKey = identifier.firstKeyOf(Ports.class); + String interfaceName = pKey.getPortName(); + + InstanceIdentifier pIdentifier = identifier.firstIdentifierOf(RouterPorts.class); + createNATFlowEntries(interfaceName, mapping, pIdentifier, routerId); + } + + private void processFloatingIPDel(final InstanceIdentifier identifier, + final IpMapping mapping) { + LOG.trace("Del event - key: {}, value: {}", identifier, mapping); + + final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId(); + final PortsKey pKey = identifier.firstKeyOf(Ports.class); + String interfaceName = pKey.getPortName(); + + InstanceIdentifier pIdentifier = identifier.firstIdentifierOf(RouterPorts.class); + removeNATFlowEntries(interfaceName, mapping, pIdentifier, routerId); + } + + private InetAddress getInetAddress(String ipAddr) { + InetAddress ipAddress = null; + try { + ipAddress = InetAddress.getByName(ipAddr); + } catch (UnknownHostException e) { + LOG.error("UnknowHostException for ip {}", ipAddr); + } + return ipAddress; + } + + private boolean validateIpMapping(IpMapping mapping) { + return getInetAddress(mapping.getInternalIp()) != null && + getInetAddress(mapping.getExternalIp()) != null; + } + + void createNATFlowEntries(String interfaceName, final IpMapping mapping, + final InstanceIdentifier pIdentifier, final String routerName) { + if(!validateIpMapping(mapping)) { + LOG.warn("Not a valid ip addresses in the mapping {}", mapping); + return; + } + + //Get the DPN on which this interface resides + BigInteger dpnId = getDpnForInterface(interfaceManager, interfaceName); + + if(dpnId.equals(BigInteger.ZERO)) { + LOG.error("No DPN for interface {}. NAT flow entries for ip mapping {} will not be installed", + interfaceName, mapping); + return; + } + + long routerId = NatUtil.getVpnId(broker, routerName); + if(routerId == NatConstants.INVALID_ID) { + LOG.warn("Could not retrieve router id for {} to create NAT Flow entries", routerName); + return; + } + + Uuid extNwId = getExtNetworkId(pIdentifier); + if(extNwId == null) { + LOG.error("External network associated with interface {} could not be retrieved", interfaceName); + LOG.error("NAT flow entries will not be installed {}", mapping); + return; + } + long vpnId = getVpnId(extNwId); + if(vpnId < 0) { + LOG.error("No VPN associated with Ext nw {}. Unable to create SNAT table entry for fixed ip {}", + extNwId, mapping.getInternalIp()); + return; + } + + //Create the DNAT and SNAT table entries + createDNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp(), routerId, vpnId); + + + String macAddr = getExternalGatewayMacAddress(routerName); + createSNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp(), vpnId, routerId, macAddr); + + handler.onAddFloatingIp(dpnId, routerName, extNwId, interfaceName, mapping.getExternalIp(), mapping + .getInternalIp()); + } + + void createNATFlowEntries(BigInteger dpnId, String interfaceName, String routerName, Uuid externalNetworkId, String internalIp, String externalIp) { + long routerId = NatUtil.getVpnId(broker, routerName); + if(routerId == NatConstants.INVALID_ID) { + LOG.warn("Could not retrieve router id for {} to create NAT Flow entries", routerName); + return; + } + long vpnId = getVpnId(externalNetworkId); + if(vpnId < 0) { + LOG.error("Unable to create SNAT table entry for fixed ip {}", internalIp); + return; + } + //Create the DNAT and SNAT table entries + createDNATTblEntry(dpnId, internalIp, externalIp, routerId, vpnId); + + String macAddr = getExternalGatewayMacAddress(routerName); + createSNATTblEntry(dpnId, internalIp, externalIp, vpnId, routerId, macAddr); + + handler.onAddFloatingIp(dpnId, routerName, externalNetworkId, interfaceName, externalIp, internalIp); + } + + private String getExternalGatewayMacAddress(String routerName) { + InstanceIdentifier routersIdentifier = NatUtil.buildRouterIdentifier(routerName); + Optional optRouters = NatUtil.read(broker, LogicalDatastoreType.CONFIGURATION, routersIdentifier); + if(optRouters.isPresent()) { + Routers routers = optRouters.get(); + return routers.getExtGwMacAddress(); + } + return ""; + } + + void removeNATFlowEntries(String interfaceName, final IpMapping mapping, + final InstanceIdentifier pIdentifier, final String routerName) { + + //Get the DPN on which this interface resides + BigInteger dpnId = getDpnForInterface(interfaceManager, interfaceName); + if(dpnId.equals(BigInteger.ZERO)) { + LOG.info("Abort processing Floating ip configuration. No DPN for port : {}", interfaceName); + return; + } + + long routerId = NatUtil.getVpnId(broker, routerName); + if(routerId == NatConstants.INVALID_ID) { + LOG.warn("Could not retrieve router id for {} to remove NAT Flow entries", routerName); + return; + } + + //Delete the DNAT and SNAT table entries + removeDNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp(), routerId); + +// Uuid extNwId = getExtNetworkId(pIdentifier); +// if(extNwId == null) { +// LOG.error("External network associated with interface {} could not be retrieved", interfaceName); +// return; +// } +// long vpnId = getVpnId(extNwId); +// if(vpnId < 0) { +// LOG.error("No VPN associated with ext nw {}. Unable to delete SNAT table entry for fixed ip {}", +// extNwId, mapping.getInternalIp()); +// return; +// } + removeSNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp()); + + long label = getOperationalIpMapping(routerName, interfaceName, mapping.getInternalIp()); + if(label < 0) { + LOG.error("Could not retrieve label for prefix {} in router {}", mapping.getInternalIp(), routerId); + return; + } + //Uuid extNwId = getExtNetworkId(pIdentifier); + Uuid extNwId = getExternalNetworkForRouter(routerName); + if(extNwId == null) { + LOG.error("External network associated with router {} could not be retrieved", routerName); + return; + } + handler.onRemoveFloatingIp(dpnId, routerName, extNwId, mapping.getExternalIp(), mapping.getInternalIp(), (int) label); + removeOperationalDS(routerName, interfaceName, mapping.getInternalIp(), mapping.getExternalIp()); + + } + + void removeNATFlowEntries(BigInteger dpnId, String interfaceName, String vpnName, String routerName, Uuid externalNetworkId, String internalIp, String externalIp) { + long routerId = NatUtil.getVpnId(broker, routerName); + if(routerId == NatConstants.INVALID_ID) { + LOG.warn("Could not retrieve router id for {} to create NAT Flow entries", routerName); + return; + } + //Delete the DNAT and SNAT table entries + removeDNATTblEntry(dpnId, internalIp, externalIp, routerId); + +// long vpnId = getVpnId(externalNetworkId); +// if(vpnId < 0) { +// LOG.error("Unable to delete SNAT table entry for fixed ip {}", internalIp); +// return; +// } + removeSNATTblEntry(dpnId, internalIp, externalIp); + + long label = getOperationalIpMapping(routerName, interfaceName, internalIp); + if(label < 0) { + LOG.error("Could not retrieve label for prefix {} in router {}", internalIp, routerId); + return; + } + //handler.onRemoveFloatingIp(dpnId, routerName, externalNetworkId, externalIp, internalIp, (int)label); + ((VpnFloatingIpHandler)handler).cleanupFibEntries(dpnId, vpnName, externalIp, label); + removeOperationalDS(routerName, interfaceName, internalIp, externalIp); + } + + private long getOperationalIpMapping(String routerId, String interfaceName, String internalIp) { + InstanceIdentifier ipMappingIdentifier = NatUtil.getIpMappingIdentifier(routerId, interfaceName, internalIp); + Optional ipMapping = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, ipMappingIdentifier); + if(ipMapping.isPresent()) { + return ipMapping.get().getLabel(); + } + return NatConstants.INVALID_ID; + } + + private Uuid getExternalNetworkForRouter(String routerId) { + InstanceIdentifier identifier = NatUtil.getRouterPortsId(routerId); + Optional optRouterPorts = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, identifier); + if(optRouterPorts.isPresent()) { + RouterPorts routerPorts = optRouterPorts.get(); + return routerPorts.getExternalNetworkId(); + } + return null; + } + + void updateOperationalDS(String routerId, String interfaceName, int label, String internalIp, String externalIp) { + + LOG.info("Updating operational DS for floating ip config : {} with label {}", internalIp, label); + InstanceIdentifier portsId = NatUtil.getPortsIdentifier(routerId, interfaceName); + Optional optPorts = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, portsId); + IpMapping ipMapping = new IpMappingBuilder().setKey(new IpMappingKey(internalIp)).setInternalIp(internalIp) + .setExternalIp(externalIp).setLabel(label).build(); + if(optPorts.isPresent()) { + LOG.debug("Ports {} entry already present. Updating ipmapping for internal ip {}", interfaceName, internalIp); + MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, portsId.child(IpMapping.class, new IpMappingKey(internalIp)), ipMapping); + } else { + LOG.debug("Adding Ports entry {} along with ipmapping {}", interfaceName, internalIp); + List ipMappings = new ArrayList<>(); + ipMappings.add(ipMapping); + Ports ports = new PortsBuilder().setKey(new PortsKey(interfaceName)).setPortName(interfaceName).setIpMapping(ipMappings).build(); + MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, portsId, ports); + } + } + + void removeOperationalDS(String routerId, String interfaceName, String internalIp, String externalIp) { + LOG.info("Remove operational DS for floating ip config: {}", internalIp); + InstanceIdentifier ipMappingId = NatUtil.getIpMappingIdentifier(routerId, interfaceName, internalIp); + MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, ipMappingId); + } + + private FlowEntity buildPreDNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId) { + + LOG.info("Bulding Delete DNAT Flow entity for ip {} ", externalIp); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PDNAT_TABLE, externalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PDNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, null, null); + + return flowEntity; + } + + + + private FlowEntity buildDNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId) { + + LOG.info("Bulding Delete DNAT Flow entity for ip {} ", externalIp); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.DNAT_TABLE, externalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.DNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, null, null); + + return flowEntity; + + } + + private FlowEntity buildPreSNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp) { + + LOG.info("Building Delete PSNAT Flow entity for ip {} ", internalIp); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PSNAT_TABLE, internalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, null, null); + + return flowEntity; + } + + private FlowEntity buildSNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp) { + + LOG.info("Building Delete SNAT Flow entity for ip {} ", internalIp); + + String flowRef = NatUtil.getFlowRef(dpId, NatConstants.SNAT_TABLE, internalIp); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.SNAT_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, null, null); + + return flowEntity; + + + } +} + diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/IPAddress.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/IPAddress.java new file mode 100644 index 00000000..fd8698cd --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/IPAddress.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +public class IPAddress { + + private String ipAddress; + private int prefixLength; + + public String getIpAddress() { + return ipAddress; + } + public int getPrefixLength() { + return prefixLength; + } + + public IPAddress(String ipAddress, int prefixLength) { + this.ipAddress = ipAddress; + this.prefixLength = prefixLength; + } +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/InterfaceStateEventListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/InterfaceStateEventListener.java new file mode 100644 index 00000000..169e452d --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/InterfaceStateEventListener.java @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import com.google.common.collect.Lists; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.*; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols; +import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.NaptSwitches; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping; +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.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.GetFixedIPsForNeutronPortInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.NeutronvpnService; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class InterfaceStateEventListener extends AbstractDataChangeListener implements AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(InterfaceStateEventListener.class); + private ListenerRegistration listenerRegistration; + private final DataBroker dataBroker; + private IMdsalApiManager mdsalManager; + private FloatingIPListener floatingIPListener; + private NaptManager naptManager; + private NeutronvpnService neutronVpnService; + + public InterfaceStateEventListener(final DataBroker db){ + super(Interface.class); + dataBroker = db; + registerListener(db); + } + + public void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + public void setFloatingIpListener(FloatingIPListener floatingIPListener) { + this.floatingIPListener = floatingIPListener; + } + + public void setNeutronVpnService(NeutronvpnService neutronVpnService) { + this.neutronVpnService = neutronVpnService; + } + + public void setNaptManager(NaptManager naptManager) { + this.naptManager = naptManager; + + } + + private void registerListener(final DataBroker db) { + try { + listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, + getWildCardPath(), InterfaceStateEventListener.this, AsyncDataBroker.DataChangeScope.SUBTREE); + } catch (final Exception e) { + LOG.error("Interface DataChange listener registration failed", e); + throw new IllegalStateException("Nexthop Manager registration Listener failed.", e); + } + } + + private InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(InterfacesState.class).child(Interface.class); + } + + @Override + protected void remove(InstanceIdentifier identifier, Interface delintrf) { + LOG.trace("NAT Service : Interface {} removed event received", delintrf); + try { + if (delintrf != null) { + String interfaceName = delintrf.getName(); + LOG.trace("NAT Service : Port removed event received for interface {} ", interfaceName); + + BigInteger dpnId = NatUtil.getDpIdFromInterface(delintrf); + LOG.trace("NAT Service : PORT_REMOVE: Interface {} down in Dpn {}", interfaceName, dpnId); + + String routerName = getRouterIdForPort(dataBroker, interfaceName); + if (routerName != null) { + processInterfaceRemoved(interfaceName, routerName); + removeSnatEntriesForPort(interfaceName,routerName); + } else { + LOG.debug("NAT Service : PORT_REMOVE: Router Id is null either Interface {} is not associated " + + "to router or failed to retrieve routerId due to exception", interfaceName); + } + } + } catch(Exception e) { + LOG.error("NAT Service : Exception caught in InterfaceOperationalStateRemove : {}", e); + } + } + + @Override + protected void update(InstanceIdentifier identifier, Interface original, Interface update) { + LOG.trace("NAT Service : Operation Interface update event - Old: {}, New: {}", original, update); + String interfaceName = update.getName(); + if (update.getOperStatus().equals(Interface.OperStatus.Up)) { + LOG.trace("NAT Service : Port UP event received for interface {} ", interfaceName); + } else if (update.getOperStatus().equals(Interface.OperStatus.Down)) { + try { + LOG.trace("NAT Service : Port DOWN event received for interface {} ", interfaceName); + + BigInteger dpnId = NatUtil.getDpIdFromInterface(update); + LOG.trace("NAT Service : PORT_DOWN: Interface {} down in Dpn {}", interfaceName, dpnId); + + String routerName = getRouterIdForPort(dataBroker, interfaceName); + if (routerName != null) { + removeSnatEntriesForPort(interfaceName,routerName); + } else { + LOG.debug("NAT Service : PORT_DOWN: Router Id is null, either Interface {} is not associated " + + "to router or failed to retrieve routerId due to exception", interfaceName); + } + } catch (Exception ex) { + LOG.error("NAT Service : Exception caught in InterfaceOperationalStateDown : {}",ex); + } + } + } + + @Override + protected void add(InstanceIdentifier identifier, Interface intrf) { + LOG.trace("NAT Service : Interface {} up event received", intrf); + try { + String interfaceName = intrf.getName(); + LOG.trace("NAT Service : Port added event received for interface {} ", interfaceName); + String routerId = getRouterIdForPort(dataBroker,interfaceName); + if (routerId != null) { + processInterfaceAdded(interfaceName, routerId); + } + } catch (Exception ex) { + LOG.error("NAT Service : Exception caught in Interface Operational State Up event: {}", ex); + } + } + + private void removeSnatEntriesForPort(String interfaceName,String routerName) { + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + if (routerId == NatConstants.INVALID_ID) { + LOG.error("NAT Service : routerId not found for routername {}",routerName); + return; + } + BigInteger naptSwitch = getNaptSwitchforRouter(dataBroker,routerName); + if (naptSwitch == null) { + LOG.error("NAT Service : NaptSwitch is not elected for router {} with Id {}",routerName,routerId); + return; + } + //getInternalIp for port + List fixedIps = getFixedIpsForPort(interfaceName); + if (fixedIps == null) { + LOG.debug("NAT Service : Internal Ips not found for InterfaceName {} in router {} with id {}",interfaceName,routerName,routerId); + return; + } + List protocolTypesList = getPortocolList(); + for (String internalIp : fixedIps) { + LOG.debug("NAT Service : Internal Ip retrieved for interface {} is {} in router with Id {}",interfaceName,internalIp,routerId); + for(ProtocolTypes protocol : protocolTypesList) { + List portList = NatUtil.getInternalIpPortListInfo(dataBroker, routerId, internalIp, protocol); + if (portList != null) { + for (Integer portnum : portList) { + //build and remove the flow in outbound table + try { + removeNatFlow(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, portnum); + } catch (Exception ex) { + LOG.error("NAT Service : Failed to remove snat flow for internalIP {} with Port {} protocol {} for routerId {} " + + "in OUTBOUNDTABLE of NaptSwitch {}: {}",internalIp,portnum,protocol,routerId,naptSwitch,ex); + } + //Get the external IP address and the port from the model + NAPTEntryEvent.Protocol proto = protocol.toString().equals(ProtocolTypes.TCP.toString()) ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP; + IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId, + internalIp, String.valueOf(portnum), proto); + if (ipPortExternal == null) { + LOG.error("Mapping for internalIp {} with port {} is not found in router with Id {}",internalIp,portnum,routerId); + return; + } + String externalIpAddress = ipPortExternal.getIpAddress(); + Integer portNumber = ipPortExternal.getPortNum(); + + //build and remove the flow in inboundtable + try { + removeNatFlow(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,routerId, externalIpAddress, portNumber); + } catch (Exception ex) { + LOG.error("NAT Service : Failed to remove snat flow internalIP {} with Port {} protocol {} for routerId {} " + + "in INBOUNDTABLE of naptSwitch {} : {}",externalIpAddress,portNumber,protocol,routerId,naptSwitch,ex); + } + + String internalIpPort = internalIp + ":" + portnum; + // delete the entry from IntExtIpPortMap DS + try { + naptManager.removeFromIpPortMapDS(routerId, internalIpPort, proto); + naptManager.removePortFromPool(internalIpPort,externalIpAddress); + } catch (Exception ex){ + LOG.error("NAPT Service : releaseIpExtPortMapping failed, Removal of ipportmap {} for router {} failed {}" , + internalIpPort, routerId, ex); + } + } + // delete the entry from SnatIntIpPortMap DS + LOG.debug("NAT Service : Removing InternalIp :{} portlist :{} for protocol :{} of router {}",internalIp,portList,protocol,routerId); + naptManager.removeFromSnatIpPortDS(routerId,internalIp); + } else { + LOG.debug("NAT Service : No {} session for interface {} with internalIP {} in router with id {}",protocol,interfaceName,internalIp,routerId); + } + } + } + } + + private String getRouterIdForPort(DataBroker dataBroker,String interfaceName) { + String vpnName = null, routerName = null; + if (NatUtil.isVpnInterfaceConfigured(dataBroker, interfaceName)) { + //getVpnInterface + VpnInterface vpnInterface = null; + try { + vpnInterface = NatUtil.getConfiguredVpnInterface(dataBroker, interfaceName); + } catch (Exception ex) { + LOG.error("NAT Service : Unable to process for interface {} as it is not configured", interfaceName); + } + if (vpnInterface != null) { + //getVpnName + try { + vpnName = vpnInterface.getVpnInstanceName(); + LOG.debug("NAT Service : Retrieved VpnName {}", vpnName); + } catch (Exception e) { + LOG.error("NAT Service : Unable to get vpnname for vpninterface {} - {}", vpnInterface, e); + } + if (vpnName != null) { + try { + routerName = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName); + } catch (Exception e) { + LOG.error("NAT Service : Unable to get routerId for vpnName {} - {}", vpnName, e); + } + if (routerName != null) { + //check router is associated to external network + if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) { + LOG.debug("NAT Service : Retreived Router Id {} for vpnname {} associated to interface {}", + routerName,vpnName,interfaceName); + return routerName; + } else { + LOG.info("NAT Service : Interface {} associated to routerId {} is not associated to external network", + interfaceName, routerName); + } + } else { + LOG.debug("Router is not associated to vpnname {} for interface {}",vpnName,interfaceName); + } + } else { + LOG.debug("NAT Service : vpnName not found for vpnInterface {} of port {}",vpnInterface,interfaceName); + } + } + } else { + LOG.debug("NAT Service : Interface {} is not a vpninterface",interfaceName); + } + return null; + } + + private List getPortocolList() { + List protocollist = Lists.newArrayList(); + protocollist.add(ProtocolTypes.TCP); + protocollist.add(ProtocolTypes.UDP); + return protocollist; + } + + private BigInteger getNaptSwitchforRouter(DataBroker broker,String routerName) { + InstanceIdentifier rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class).child + (RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build(); + Optional routerToNaptSwitchData = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, rtrNaptSw); + if (routerToNaptSwitchData.isPresent()) { + RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get(); + return routerToNaptSwitchInstance.getPrimarySwitchId(); + } + return null; + } + + private void removeNatFlow(BigInteger dpnId, short tableId,Long routerId, String ipAddress,int ipPort) { + + String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), ipAddress, ipPort); + FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef); + + mdsalManager.removeFlow(snatFlowEntity); + LOG.debug("NAT Service : Removed the flow in table {} for the switch with the DPN ID {} for router {} ip {} port {}", + tableId,dpnId,routerId,ipAddress,ipPort); + } + + private void processInterfaceAdded(String portName, String rtrId) { + LOG.trace("Processing Interface Add Event for interface {}", portName); + String routerId = getRouterIdForPort(dataBroker, portName); + List ipMappingList = getIpMappingForPortName(portName, routerId); + if (ipMappingList == null || ipMappingList.isEmpty()) { + LOG.trace("Ip Mapping list is empty/null for portname {}", portName); + return; + } + InstanceIdentifier pIdentifier = NatUtil.buildRouterPortsIdentifier(routerId); + for (IpMapping ipMapping : ipMappingList) { + floatingIPListener.createNATFlowEntries(portName, ipMapping, pIdentifier, routerId); + } + } + + private void processInterfaceRemoved(String portName, String rtrId) { + LOG.trace("Processing Interface Removed Event for interface {}", portName); + String routerId = getRouterIdForPort(dataBroker, portName); + List ipMappingList = getIpMappingForPortName(portName, routerId); + if (ipMappingList == null || ipMappingList.isEmpty()) { + LOG.trace("Ip Mapping list is empty/null for portName {}", portName); + return; + } + InstanceIdentifier pIdentifier = NatUtil.buildRouterPortsIdentifier(routerId); + for (IpMapping ipMapping : ipMappingList) { + floatingIPListener.removeNATFlowEntries(portName, ipMapping, pIdentifier, routerId); + } + } + + private List getIpMappingForPortName(String portName, String routerId) { + InstanceIdentifier portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName); + Optional port = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier); + if(!port.isPresent()) { + LOG.error("NAT Service : Unable to read router port entry for router ID {} and port name {}", routerId, portName); + return null; + } + List ipMappingList = port.get().getIpMapping(); + return ipMappingList; + } + + private List getFixedIpsForPort (String interfname) { + LOG.debug("getFixedIpsForPort method is called for interface {}",interfname); + try { + Future> result = + neutronVpnService.getFixedIPsForNeutronPort(new GetFixedIPsForNeutronPortInputBuilder() + .setPortId(new Uuid(interfname)).build()); + + RpcResult rpcResult = result.get(); + if(!rpcResult.isSuccessful()) { + LOG.warn("NAT Service : RPC Call to GetFixedIPsForNeutronPortOutput returned with Errors {}", rpcResult.getErrors()); + } else { + return rpcResult.getResult().getFixedIPs(); + } + } catch (InterruptedException | ExecutionException | NullPointerException ex ) { + LOG.error("NAT Service : Exception while receiving fixedIps for port {}",interfname); + } + return null; + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + LOG.error("NAT Service : Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + LOG.info("NAT Service : Interface listener Closed"); + } +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTEntryEvent.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTEntryEvent.java new file mode 100644 index 00000000..8a16f7c8 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTEntryEvent.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + + +public class NAPTEntryEvent { + private String ipAddress; + private int portNumber; + private Long routerId; + private Operation op; + private Protocol protocol; + + public String getIpAddress() { + return ipAddress; + } + + public int getPortNumber() { + return portNumber; + } + + public Long getRouterId() { + return routerId; + } + + public Operation getOperation() { + return op; + } + + public Protocol getProtocol() { + return protocol; + } + + NAPTEntryEvent(String ipAddress, int portNumber, Long routerId, Operation op, Protocol protocol){ + this.ipAddress = ipAddress; + this.portNumber = portNumber; + this.routerId = routerId; + this.op = op; + this.protocol = protocol; + } + + public enum Operation{ + ADD, DELETE; + } + + public enum Protocol{ + TCP, UDP; + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTSwitchSelector.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTSwitchSelector.java new file mode 100644 index 00000000..b7fd7d31 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTSwitchSelector.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData; +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.VpnInstanceOpDataEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; +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.NaptSwitchesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +public class NAPTSwitchSelector { + private static final Logger LOG = LoggerFactory.getLogger(NAPTSwitchSelector.class); + + private DataBroker dataBroker; + public NAPTSwitchSelector(DataBroker dataBroker) { + this.dataBroker = dataBroker; + } + + BigInteger selectNewNAPTSwitch(String routerName) { + LOG.info("NAT Service : Select a new NAPT switch for router {}", routerName); + Map naptSwitchWeights = constructNAPTSwitches(); + List routerSwitches = getDpnsForVpn(routerName); + if(routerSwitches.isEmpty()) { + LOG.debug("NAT Service : No dpns that are part of router {}", routerName); + LOG.warn("NAT Service : NAPT switch selection stopped due to no dpns scenario for router {}", routerName); + return BigInteger.ZERO; + } + + Set switchWeights = new TreeSet<>(); + for(BigInteger dpn : routerSwitches) { + if(naptSwitchWeights.get(dpn) != null) { + switchWeights.add(new SwitchWeight(dpn, naptSwitchWeights.get(dpn))); + } else { + switchWeights.add(new SwitchWeight(dpn, 0)); + } + } + + BigInteger primarySwitch; + + if(!switchWeights.isEmpty()) { + + LOG.debug("NAT Service : Current switch weights for router {} - {}", routerName, switchWeights); + + Iterator it = switchWeights.iterator(); + List routerToNaptSwitchList = new ArrayList<>(); + RouterToNaptSwitchBuilder routerToNaptSwitchBuilder = new RouterToNaptSwitchBuilder().setRouterName(routerName); + if ( switchWeights.size() == 1 ) + { + SwitchWeight singleSwitchWeight = null; + while(it.hasNext() ) { + singleSwitchWeight = it.next(); + } + primarySwitch = singleSwitchWeight.getSwitch(); + routerToNaptSwitchBuilder.setPrimarySwitchId(primarySwitch); + routerToNaptSwitchList.add(routerToNaptSwitchBuilder.build()); + NaptSwitches naptSwitches = new NaptSwitchesBuilder().setRouterToNaptSwitch(routerToNaptSwitchList).build(); + MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier(), naptSwitches); + + LOG.debug( "NAT Service : successful addition of RouterToNaptSwitch to napt-switches container for single switch" ); + return primarySwitch; + } + else + { + SwitchWeight firstSwitchWeight = null; + while(it.hasNext() ) { + firstSwitchWeight = it.next(); + } + primarySwitch = firstSwitchWeight.getSwitch(); + routerToNaptSwitchBuilder.setPrimarySwitchId(primarySwitch); + routerToNaptSwitchList.add(routerToNaptSwitchBuilder.build()); + NaptSwitches naptSwitches = new NaptSwitchesBuilder().setRouterToNaptSwitch(routerToNaptSwitchList).build(); + MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier(), naptSwitches); + + LOG.debug( "NAT Service : successful addition of RouterToNaptSwitch to napt-switches container"); + return primarySwitch; + } + } else { + + primarySwitch = BigInteger.ZERO; + + LOG.debug("NAT Service : switchWeights empty, primarySwitch: {} ", primarySwitch); + return primarySwitch; + } + + + } + + private Map constructNAPTSwitches() { + Optional optNaptSwitches = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier()); + Map switchWeights = new HashMap<>(); + + if(optNaptSwitches.isPresent()) { + NaptSwitches naptSwitches = optNaptSwitches.get(); + List routerToNaptSwitches = naptSwitches.getRouterToNaptSwitch(); + + for(RouterToNaptSwitch naptSwitch : routerToNaptSwitches) { + BigInteger primarySwitch = naptSwitch.getPrimarySwitchId(); + //update weight + Integer weight = switchWeights.get(primarySwitch); + if(weight == null) { + switchWeights.put(primarySwitch, 1); + } else { + switchWeights.put(primarySwitch, ++weight); + } + } + } + return switchWeights; + } + + private InstanceIdentifier getNaptSwitchesIdentifier() { + return InstanceIdentifier.create(NaptSwitches.class); + } + + public List getDpnsForVpn(String routerName ) { + LOG.debug( "getVpnToDpnList called for RouterName {}", routerName ); + + InstanceIdentifier id = InstanceIdentifier.builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(routerName)) + .build(); + + List dpnsInVpn = new ArrayList<>(); + Optional vpnInstanceOpData = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id); + + if(vpnInstanceOpData.isPresent()) { + LOG.debug( "NATService : getVpnToDpnList able to fetch vpnInstanceOpData" ); + VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnInstanceOpData.get(); + List vpnDpnList = vpnInstanceOpDataEntry.getVpnToDpnList(); + if(vpnDpnList != null) { + for(VpnToDpnList vpnToDpn: vpnDpnList) { + dpnsInVpn.add(vpnToDpn.getDpnId()); + } + } + } + + LOG.debug( "getVpnToDpnList returning vpnDpnList {}", dpnsInVpn); + return dpnsInVpn; + } + + private static class SwitchWeight implements Comparable + { + private BigInteger swich; + private int weight; + + public SwitchWeight( BigInteger swich, int weight ) + { + this.swich = swich; + this.weight = weight; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((swich == null) ? 0 : swich.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SwitchWeight other = (SwitchWeight) obj; + if (swich == null) { + if (other.swich != null) + return false; + } else if (!swich.equals(other.swich)) + return false; + return true; + } + + public BigInteger getSwitch() { + return swich; + } + + public int getWeight() { + return weight; + } + + public void incrementWeight() { + ++ weight; + } + + @Override + public int compareTo(SwitchWeight o) { + return o.getWeight() - weight; + } + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptEventHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptEventHandler.java new file mode 100644 index 00000000..f8e2a674 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptEventHandler.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.vpnservice.mdsalutil.*; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NaptEventHandler { + private NaptManager naptManager; + private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class); + private static IMdsalApiManager mdsalManager; + private DataBroker dataBroker; + + public NaptEventHandler(final DataBroker dataBroker) { + this.dataBroker = dataBroker; + } + + public void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + public void setNaptManager(NaptManager naptManager) { + this.naptManager = naptManager; + } + + + public void handleEvent(NAPTEntryEvent naptEntryEvent){ + /* + Flow programming logic of the OUTBOUND NAPT TABLE : + 1) Get the internal IP address, port number, router ID from the event. + 2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port. + 3) Build the flow for replacing the Internal IP and port with the External IP and port. + a) Write the matching criteria. + b) Match the router ID in the metadata. + d) Write the VPN ID to the metadata. + e) Write the other data. + f) Set the apply actions instruction with the action setfield. + 4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic. + + Flow programming logic of the INBOUND NAPT TABLE : + Same as Outbound table logic except that : + 1) Build the flow for replacing the External IP and port with the Internal IP and port. + 2) Match the VPN ID in the metadata. + 3) Write the router ID to the metadata. + 5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic. + */ + Long routerId = naptEntryEvent.getRouterId(); + LOG.info("NAT Service : handleEvent() entry for IP {}, port {}, routerID {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId); + BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId); + if(dpnId == null ){ + LOG.error("NAT Service : dpnId is null"); + return; + } + if(naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) { + LOG.debug("NAT Service : Inside Add operation of NaptEventHandler"); + + //Get the external network ID from the ExternalRouter model + Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId); + if(networkId == null ){ + LOG.error("NAT Service : networkId is null"); + return; + } + + //Get the VPN ID from the ExternalNetworks model + Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId); + if(vpnUuid == null ){ + LOG.error("NAT Service : vpnUuid is null"); + return; + } + Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue()); + + //Get the internal IpAddress, internal port number from the event + String internalIpAddress = naptEntryEvent.getIpAddress(); + int internalPort = naptEntryEvent.getPortNumber(); + SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort); + NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol(); + + //Get the external IP address for the corresponding internal IP address + SessionAddress externalAddress = naptManager.getExternalAddressMapping(routerId, internalAddress, naptEntryEvent.getProtocol()); + if(externalAddress == null ){ + LOG.error("NAT Service : externalAddress is null"); + return; + } + + //Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables + buildAndInstallNatFlows(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, vpnId, routerId, internalAddress, externalAddress, protocol); + buildAndInstallNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnId, routerId, externalAddress, internalAddress, protocol); + + }else{ + LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler"); + removeNatFlows(dpnId, routerId, naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber()); + } + + LOG.info("NAT Service : handleNaptEvent() exited for IP, port, routerID : {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId); + } + + public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId, SessionAddress actualSourceAddress, + SessionAddress translatedSourceAddress, NAPTEntryEvent.Protocol protocol){ + LOG.debug("NAT Service : Build and install NAPT flows in InBound and OutBound tables for dpnId {} and routerId {}", dpnId, routerId); + //Build the flow for replacing the actual IP and port with the translated IP and port. + String actualIp = actualSourceAddress.getIpAddress(); + int actualPort = actualSourceAddress.getPortNumber(); + String translatedIp = translatedSourceAddress.getIpAddress(); + String translatedPort = String.valueOf(translatedSourceAddress.getPortNumber()); + int idleTimeout=0; + if(tableId == NatConstants.OUTBOUND_NAPT_TABLE) { + idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT; + } + long metaDataValue = routerId; + String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metaDataValue), actualIp, actualPort); + + FlowEntity snatFlowEntity = MDSALUtil.buildFlowEntity(dpnId, tableId, switchFlowRef, NatConstants.DEFAULT_NAPT_FLOW_PRIORITY, NatConstants.NAPT_FLOW_NAME, + idleTimeout, 0, NatUtil.getCookieNaptFlow(metaDataValue), buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, routerId, vpnId), + buildAndGetSetActionInstructionInfo(translatedIp, translatedPort, routerId, vpnId, tableId, protocol)); + + snatFlowEntity.setSendFlowRemFlag(true); + + LOG.debug("NAT Service : Installing the NAPT flow in the table {} for the switch with the DPN ID {} ", tableId, dpnId); + mdsalManager.installFlow(snatFlowEntity); + } + + private static List buildAndGetMatchInfo(String ip, int port, short tableId, NAPTEntryEvent.Protocol protocol, long segmentId, long vpnId){ + MatchInfo ipMatchInfo = null; + MatchInfo portMatchInfo = null; + MatchInfo protocolMatchInfo = null; + InetAddress ipAddress = null; + String ipAddressAsString = null; + try { + ipAddress = InetAddress.getByName(ip); + ipAddressAsString = ipAddress.getHostAddress(); + + } catch (UnknownHostException e) { + LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed to build NAPT Flow for ip {}", ipAddress); + return null; + } + + MatchInfo metaDataMatchInfo; + if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){ + ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_source, new String[] {ipAddressAsString, "32" }); + if(protocol == NAPTEntryEvent.Protocol.TCP) { + protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()}); + portMatchInfo = new MatchInfo(MatchFieldType.tcp_src, new long[]{port}); + } else if(protocol == NAPTEntryEvent.Protocol.UDP) { + protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()}); + portMatchInfo = new MatchInfo(MatchFieldType.udp_src, new long[]{port}); + } + metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID}); + }else{ + ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_destination, new String[] {ipAddressAsString, "32" }); + if(protocol == NAPTEntryEvent.Protocol.TCP) { + protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()}); + portMatchInfo = new MatchInfo(MatchFieldType.tcp_dst, new long[]{port}); + } else if(protocol == NAPTEntryEvent.Protocol.UDP) { + protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()}); + portMatchInfo = new MatchInfo(MatchFieldType.udp_dst, new long[]{port}); + } + metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID}); + } + ArrayList matchInfo = new ArrayList<>(); + matchInfo.add(new MatchInfo(MatchFieldType.eth_type, new long[] { 0x0800L })); + matchInfo.add(ipMatchInfo); + matchInfo.add(protocolMatchInfo); + matchInfo.add(portMatchInfo); + matchInfo.add(metaDataMatchInfo); + return matchInfo; + } + + private static List buildAndGetSetActionInstructionInfo(String ipAddress, String port, long routerId, long vpnId, short tableId, NAPTEntryEvent.Protocol protocol) { + ActionInfo ipActionInfo = null; + ActionInfo portActionInfo = null; + ArrayList listActionInfo = new ArrayList<>(); + ArrayList instructionInfo = new ArrayList<>(); + + if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){ + ipActionInfo = new ActionInfo(ActionType.set_source_ip, new String[] {ipAddress}); + if(protocol == NAPTEntryEvent.Protocol.TCP) { + portActionInfo = new ActionInfo( ActionType.set_tcp_source_port, new String[] {port}); + } else if(protocol == NAPTEntryEvent.Protocol.UDP) { + portActionInfo = new ActionInfo( ActionType.set_udp_source_port, new String[] {port}); + } + instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID})); + }else{ + ipActionInfo = new ActionInfo(ActionType.set_destination_ip, new String[] {ipAddress}); + if(protocol == NAPTEntryEvent.Protocol.TCP) { + portActionInfo = new ActionInfo( ActionType.set_tcp_destination_port, new String[] {port}); + } else if(protocol == NAPTEntryEvent.Protocol.UDP) { + portActionInfo = new ActionInfo( ActionType.set_udp_destination_port, new String[] {port}); + } + instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID})); + } + + listActionInfo.add(ipActionInfo); + listActionInfo.add(portActionInfo); + + instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo)); + instructionInfo.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.NAPT_PFIB_TABLE })); + + return instructionInfo; + } + + private void removeNatFlows(BigInteger dpnId, long routerId, String externalIp, int externalPort){ + LOG.debug("NAT Service : Remove NAPT flows for dpnId {} and routerId {}", dpnId, routerId); + + //Build the flow with the externalPort IP and port as the match info. + String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort); + FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, switchFlowRef); + LOG.debug("NAT Service : Remove the flow in the table {} for the switch with the DPN ID {}", NatConstants.INBOUND_NAPT_TABLE, dpnId); + mdsalManager.removeFlow(snatFlowEntity); + + } + +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptFlowRemovedEventHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptFlowRemovedEventHandler.java new file mode 100644 index 00000000..f0206e2f --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptFlowRemovedEventHandler.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.RemovedReasonFlags; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.TcpMatchFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.UdpMatchFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer4Match; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import java.math.BigInteger; + +public class NaptFlowRemovedEventHandler implements SalFlowListener{ + private DataBroker dataBroker; + private final EventDispatcher naptEventdispatcher; + private static final Logger LOG = LoggerFactory.getLogger(NaptFlowRemovedEventHandler.class); + private final NaptPacketInHandler naptPacketInHandler; + private IMdsalApiManager mdsalManager; + private NaptManager naptManager; + + public NaptFlowRemovedEventHandler(EventDispatcher eventDispatcher, DataBroker dataBroker, NaptPacketInHandler handler, + IMdsalApiManager mdsalManager, NaptManager naptManager) { + this.naptEventdispatcher = eventDispatcher; + this.dataBroker = dataBroker; + this.naptPacketInHandler = handler; + this.mdsalManager = mdsalManager; + this.naptManager = naptManager; + } + + @Override + public void onSwitchFlowRemoved(SwitchFlowRemoved switchFlowRemoved) { + +/* + If the removed flow is from the OUTBOUND NAPT table : + 1) Get the ActionInfo of the flow. + 2) From the ActionInfo of the flow get the internal IP address, port and the protocol. + 3) Get the Metadata matching info of the flow. + 4) From the Metadata matching info of the flow get router ID. + 5) Querry the container intext-ip-port-map using the router ID + and the internal IP address, port to get the external IP address, port + 6) Instantiate an NaptEntry event and populate the external IP address, port and the router ID. + 7) Place the NaptEntry event to the queue. +*/ + + short tableId = switchFlowRemoved.getTableId(); + RemovedReasonFlags removedReasonFlag = switchFlowRemoved.getRemovedReason(); + + if (tableId == NatConstants.OUTBOUND_NAPT_TABLE) { + LOG.info("NaptFlowRemovedEventHandler : onSwitchFlowRemoved() entry"); + + //Get the internal internal IP address and the port number from the IPv4 match. + Ipv4Prefix internalIpv4Address = null; + Layer3Match layer3Match = switchFlowRemoved.getMatch().getLayer3Match(); + if (layer3Match instanceof Ipv4Match) { + Ipv4Match internalIpv4Match = (Ipv4Match) layer3Match; + internalIpv4Address = internalIpv4Match.getIpv4Source(); + } + if(internalIpv4Address == null){ + LOG.error("NaptFlowRemovedEventHandler : Matching internal IP is null while retrieving the value from the Outbound NAPT flow"); + return; + } + //Get the internal IP as a string + String internalIpv4AddressAsString = internalIpv4Address.getValue(); + String[] internalIpv4AddressParts = internalIpv4AddressAsString.split("/"); + String internalIpv4HostAddress = null; + if(internalIpv4AddressParts.length >= 1){ + internalIpv4HostAddress = internalIpv4AddressParts[0]; + } + + //Get the protocol from the layer4 match + NAPTEntryEvent.Protocol protocol = null; + Integer internalPortNumber = null; + Layer4Match layer4Match = switchFlowRemoved.getMatch().getLayer4Match(); + if (layer4Match instanceof TcpMatch) { + TcpMatchFields tcpMatchFields = (TcpMatchFields)layer4Match; + internalPortNumber = tcpMatchFields.getTcpSourcePort().getValue(); + protocol = NAPTEntryEvent.Protocol.TCP; + }else if (layer4Match instanceof UdpMatch){ + UdpMatchFields udpMatchFields = (UdpMatchFields)layer4Match; + internalPortNumber = udpMatchFields.getUdpSourcePort().getValue(); + protocol = NAPTEntryEvent.Protocol.UDP; + } + if(protocol == null){ + LOG.error("NaptFlowRemovedEventHandler : Matching protocol is null while retrieving the value from the Outbound NAPT flow"); + return; + } + + //Get the router ID from the metadata. + Long routerId; + BigInteger metadata = switchFlowRemoved.getMatch().getMetadata().getMetadata(); + if(MetaDataUtil.getNatRouterIdFromMetadata(metadata) != 0) { + routerId = MetaDataUtil.getNatRouterIdFromMetadata(metadata); + }else { + LOG.error("NaptFlowRemovedEventHandler : Null exception while retrieving routerId"); + return; + } + + //Get the external IP address and the port from the model + IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId, internalIpv4HostAddress, internalPortNumber.toString(), protocol); + if(ipPortExternal == null){ + LOG.error("NaptFlowRemovedEventHandler : IpPortExternal is null while querried from the model"); + return; + } + String externalIpAddress = ipPortExternal.getIpAddress(); + int externalPortNumber = ipPortExternal.getPortNum(); + + //Create an NAPT event and place it in the queue. + NAPTEntryEvent naptEntryEvent = new NAPTEntryEvent(externalIpAddress, externalPortNumber, routerId, NAPTEntryEvent.Operation.DELETE, protocol); + naptEventdispatcher.addNaptEvent(naptEntryEvent); + + //Get the DPN ID from the Node + InstanceIdentifier nodeRef = switchFlowRemoved.getNode().getValue().firstIdentifierOf(Node.class); + String dpn = nodeRef.firstKeyOf(Node.class).getId().getValue(); + BigInteger dpnId = getDpnId(dpn); + + //Inform the MDSAL manager to inform about the flow removal. + String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metadata), internalIpv4HostAddress, internalPortNumber); + LOG.debug("NaptFlowRemovedEventHandler : DPN ID {}, Metadata {}, SwitchFlowRef {}, internalIpv4HostAddress{}", dpnId, metadata, switchFlowRef, internalIpv4AddressAsString); + FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef); + mdsalManager.removeFlow(snatFlowEntity); + + if(removedReasonFlag.isIDLETIMEOUT()) { + LOG.debug("Received flow removed notification due to idleTimeout of flow from switch for flowref {}",switchFlowRef); + //Remove the SourceIP:Port key from the Napt packet handler map. + String internalIpPortKey = internalIpv4HostAddress + ":" + internalPortNumber; + naptPacketInHandler.removeIncomingPacketMap(internalIpPortKey); + + //Remove the mapping of internal fixed ip/port to external ip/port from the datastore. + SessionAddress internalSessionAddress = new SessionAddress(internalIpv4HostAddress, internalPortNumber); + naptManager.releaseIpExtPortMapping(routerId, internalSessionAddress, protocol); + } else { + LOG.debug("Received flow removed notification due to flowdelete from switch for flowref {}",switchFlowRef); + } + + LOG.info("NaptFlowRemovedEventHandler : onSwitchFlowRemoved() exit"); + } + } + + private BigInteger getDpnId(String node) { + //openflow:1] + String temp[] = node.split(":"); + BigInteger dpnId = new BigInteger(temp[1]); + return dpnId; + + } + + @Override + public void onFlowAdded(FlowAdded arg0) { + // TODO Auto-generated method stub + + } + + @Override + public void onFlowRemoved(FlowRemoved arg0) { + // TODO Auto-generated method stub + + } + + @Override + public void onFlowUpdated(FlowUpdated arg0) { + // TODO Auto-generated method stub + + } + + @Override + public void onNodeErrorNotification(NodeErrorNotification arg0) { + // TODO Auto-generated method stub + + } + + @Override + public void onNodeExperimenterErrorNotification(NodeExperimenterErrorNotification arg0) { + // TODO Auto-generated method stub + + } + +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptManager.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptManager.java new file mode 100644 index 00000000..db6a2b19 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptManager.java @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +/* + * Created eyugsar 2016/12/1 + */ + +package org.opendaylight.vpnservice.natservice.internal; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.ExecutionException; + +import com.google.common.collect.Lists; +import org.apache.commons.net.util.SubnetUtils; +import org.apache.commons.net.util.SubnetUtils.SubnetInfo; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpMap; +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.SnatintIpPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey; +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.IpPortMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolTypeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternalBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIds; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMapKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPortKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.UncheckedExecutionException; + +public class NaptManager { + private static final Logger LOG = LoggerFactory.getLogger(NaptManager.class); + private final DataBroker broker; + private IdManagerService idManager; + private static final long LOW_PORT = 49152L; + private static final long HIGH_PORT = 65535L; + private static boolean EXTSUBNET_FLAG = false; + private static boolean NEXT_EXTIP_FLAG = false; + + public NaptManager(final DataBroker db) { + this.broker = db; + } + + public void setIdManager(IdManagerService idManager) { + this.idManager = idManager; + } + + protected void createNaptPortPool(String PoolName) { + LOG.debug("NAPT Service : createPortPool requested for : {}", PoolName); + CreateIdPoolInput createPool = new CreateIdPoolInputBuilder() + .setPoolName(PoolName) + .setLow(LOW_PORT) + .setHigh(HIGH_PORT) + .build(); + try { + Future> result = idManager.createIdPool(createPool); + if ((result != null) && (result.get().isSuccessful())) { + LOG.debug("NAPT Service : Created PortPool"); + } else { + LOG.error("NAPT Service : Unable to create PortPool"); + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("Failed to create PortPool for NAPT Service",e); + } + } + + // 1. napt service functions + /** + * this method is used to inform this service of what external IP address to be used + * as mapping when requested one for the internal IP address given in the input + * @param segmentId – segmentation in which the mapping to be used. Eg; routerid + * @param internal subnet prefix or ip address + * @param external subnet prefix or ip address + */ + + public void registerMapping(long segmentId, IPAddress internal, IPAddress external) { + + LOG.debug("NAPT Service : registerMapping called with segmentid {}, internalIp {}, prefix {}, externalIp {} and prefix {} ", segmentId, internal.getIpAddress(), + internal.getPrefixLength(), external.getIpAddress(), external.getPrefixLength()); + // Create Pool per ExternalIp and not for all IPs in the subnet. Create new Pools during getExternalAddressMapping if exhausted. + String externalIpPool; + if (external.getPrefixLength() !=0 && external.getPrefixLength() != NatConstants.DEFAULT_PREFIX) { // subnet case + String externalSubnet = new StringBuilder(64).append(external.getIpAddress()).append("/").append(external.getPrefixLength()).toString(); + LOG.debug("NAPT Service : externalSubnet is : {}", externalSubnet); + SubnetUtils subnetUtils = new SubnetUtils(externalSubnet); + SubnetInfo subnetInfo = subnetUtils.getInfo(); + externalIpPool = subnetInfo.getLowAddress(); + } else { // ip case + externalIpPool = external.getIpAddress(); + } + createNaptPortPool(externalIpPool); + + // Store the ip to ip map in Operational DS + String internalIp = internal.getIpAddress(); + if(internal.getPrefixLength() != 0) { + internalIp = new StringBuilder(64).append(internal.getIpAddress()).append("/").append(internal.getPrefixLength()).toString(); + } + String externalIp = external.getIpAddress(); + if(external.getPrefixLength() != 0) { + externalIp = new StringBuilder(64).append(external.getIpAddress()).append("/").append(external.getPrefixLength()).toString(); + } + IpMap ipm = new IpMapBuilder().setKey(new IpMapKey(internalIp)).setInternalIp(internalIp).setExternalIp(externalIp).build(); + MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, getIpMapIdentifier(segmentId, internalIp), ipm); + LOG.debug("NAPT Service : registerMapping exit after updating DS with internalIP {}, externalIP {}", internalIp, externalIp); + } + + + /** + * method to get external ip/port mapping when provided with internal ip/port pair + * If already a mapping exist for the given input, then the existing mapping is returned + * instead of overwriting with new ip/port pair. + * @param segmentId + * @param sourceAddress - internal ip address/port pair + * @return external ip address/port + */ + public SessionAddress getExternalAddressMapping(long segmentId, SessionAddress sourceAddress, NAPTEntryEvent.Protocol protocol) { + LOG.debug("NAPT Service : getExternalAddressMapping called with segmentId {}, internalIp {} and port {}", + segmentId, sourceAddress.getIpAddress(), sourceAddress.getPortNumber()); + /* + 1. Get Internal IP, Port in IP:Port format + 2. Inside DB with routerId get the list of entries and check if it matches with existing IP:Port + 3. If True return SessionAddress of ExternalIp and Port + 4. Else check ip Map and Form the ExternalIp and Port and update DB and then return ExternalIp and Port + */ + + //SessionAddress externalIpPort = new SessionAddress(); + String internalIpPort = new StringBuilder(64).append(sourceAddress.getIpAddress()).append(":").append(sourceAddress.getPortNumber()).toString(); + + // First check existing Port Map. + SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort ,protocol); + if(existingIpPort != null) { + // populate externalIpPort from IpPortMap and return + LOG.debug("NAPT Service : getExternalAddressMapping successfully returning existingIpPort as {} and {}", existingIpPort.getIpAddress(), existingIpPort.getPortNumber()); + return existingIpPort; + } else { + // Now check in ip-map + String externalIp = checkIpMap(segmentId, sourceAddress.getIpAddress()); + if(externalIp == null) { + LOG.error("NAPT Service : getExternalAddressMapping, Unexpected error, internal to external ip map does not exist"); + return null; + } else { + /* Logic assuming internalIp is always ip and not subnet + * case 1: externalIp is ip + * a) goto externalIp pool and getPort and return + * b) else return error + * case 2: externalIp is subnet + * a) Take first externalIp and goto that Pool and getPort + * if port -> return + * else Take second externalIp and create that Pool and getPort + * if port ->return + * else + * Continue same with third externalIp till we exhaust subnet + * b) Nothing worked return error + */ + SubnetUtils externalIpSubnet; + List allIps = new ArrayList(); + String subnetPrefix = "/" + String.valueOf(NatConstants.DEFAULT_PREFIX); + if( !externalIp.contains(subnetPrefix) ) { + EXTSUBNET_FLAG = true; + externalIpSubnet = new SubnetUtils(externalIp); + allIps = Arrays.asList(externalIpSubnet.getInfo().getAllAddresses()); + LOG.debug("NAPT Service : total count of externalIps available {}", externalIpSubnet.getInfo().getAddressCount()); + } else { + LOG.debug("NAPT Service : getExternalAddress single ip case"); + if(externalIp.contains(subnetPrefix)) { + String[] externalIpSplit = externalIp.split("/"); + String extIp = externalIpSplit[0]; + externalIp = extIp; //remove /32 what we got from checkIpMap + } + allIps.add(externalIp); + } + + for(String extIp : allIps) { + LOG.info("NAPT Service : Looping externalIPs with externalIP now as {}", extIp); + if(NEXT_EXTIP_FLAG) { + createNaptPortPool(extIp); + LOG.debug("NAPT Service : Created Pool for next Ext IP {}", extIp); + } + AllocateIdInput getIdInput = new AllocateIdInputBuilder() + .setPoolName(extIp).setIdKey(internalIpPort) + .build(); + try { + Future> result = idManager.allocateId(getIdInput); + RpcResult rpcResult; + if ((result != null) && (result.get().isSuccessful())) { + LOG.debug("NAPT Service : Got id from idManager"); + rpcResult = result.get(); + } else { + LOG.error("NAPT Service : getExternalAddressMapping, idManager could not allocate id retry if subnet"); + if(!EXTSUBNET_FLAG) { + LOG.error("NAPT Service : getExternalAddressMapping returning null for single IP case, may be ports exhausted"); + return null; + } + LOG.debug("NAPT Service : Could be ports exhausted case, try with another externalIP if possible"); + NEXT_EXTIP_FLAG = true; + continue; + } + int extPort= rpcResult.getResult().getIdValue().intValue(); + SessionAddress externalIpPort = new SessionAddress(extIp, extPort); + // Write to ip-port-map before returning + IpPortExternalBuilder ipExt = new IpPortExternalBuilder(); + IpPortExternal ipPortExt = ipExt.setIpAddress(extIp).setPortNum(extPort).build(); + IpPortMap ipm = new IpPortMapBuilder().setKey(new IpPortMapKey(internalIpPort)) + .setIpPortInternal(internalIpPort).setIpPortExternal(ipPortExt).build(); + LOG.debug("NAPT Service : getExternalAddressMapping writing into ip-port-map with externalIP {} and port {}", + ipPortExt.getIpAddress(), ipPortExt.getPortNum()); + try { + MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, + getIpPortMapIdentifier(segmentId, internalIpPort, protocol), ipm); + } catch (UncheckedExecutionException uee) { + LOG.error("NAPT Service : Failed to write into ip-port-map with exception {}", uee.getMessage() ); + } + + // Write to snat-internal-ip-port-info + String internalIpAddress = sourceAddress.getIpAddress(); + int ipPort = sourceAddress.getPortNumber(); + ProtocolTypes protocolType = NatUtil.getProtocolType(protocol); + List portList = NatUtil.getInternalIpPortListInfo(broker,segmentId,internalIpAddress,protocolType); + if (portList == null) { + portList = Lists.newArrayList(); + } + portList.add(ipPort); + + IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder(); + IntIpProtoType intIpProtocolType = builder.setKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build(); + try { + MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, NatUtil.buildSnatIntIpPortIdentifier(segmentId, internalIpAddress, protocolType), intIpProtocolType); + } catch (Exception ex) { + LOG.error("NAPT Service : Failed to write into snat-internal-ip-port-info with exception {}", ex.getMessage() ); + } + + LOG.debug("NAPT Service : getExternalAddressMapping successfully returning externalIP {} and port {}", + externalIpPort.getIpAddress(), externalIpPort.getPortNumber()); + return externalIpPort; + } catch(InterruptedException | ExecutionException e) { + LOG.error("NAPT Service : getExternalAddressMapping, Exception caught {}",e); + return null; + } + }// end of for loop + }// end of else ipmap present + }// end of else check ipmap + LOG.error("NAPT Service: getExternalAddressMapping returning null, nothing worked or externalIPs exhausted"); + return null; + } + + + /** + * release the existing mapping of internal ip/port to external ip/port pair + * if no mapping exist for given internal ip/port, it returns false + * @param segmentId + * @param address + * @return true if mapping exist and the mapping is removed successfully + */ + + public boolean releaseAddressMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) { + + LOG.debug("NAPT Service : releaseAddressMapping called with segmentId {}, internalIP {}, port {}", segmentId, address.getIpAddress(), address.getPortNumber()); + // delete entry from IpPort Map and IP Map if exists + String internalIpPort = new StringBuilder(64).append(address.getIpAddress()).append(":").append(address.getPortNumber()).toString(); + SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol); + if(existingIpPort != null) { + // delete the entry from IpPortMap DS + try { + removeFromIpPortMapDS(segmentId, internalIpPort , protocol); + } catch (Exception e){ + LOG.error("NAPT Service : releaseAddressMapping failed, Removal of ipportmap {} for router {} failed {}" , internalIpPort, segmentId, e); + return false; + } + } else { + LOG.error("NAPT Service : releaseAddressMapping failed, segmentId {} and internalIpPort {} not found in IpPortMap DS", segmentId, internalIpPort); + return false; + } + String existingIp = checkIpMap(segmentId, address.getIpAddress()); + if(existingIp != null) { + // delete the entry from IpMap DS + try { + removeFromIpMapDS(segmentId, address.getIpAddress()); + } catch (Exception e){ + LOG.error("NAPT Service : Removal of ipmap {} for router {} failed {}" , address.getIpAddress(), segmentId, e); + return false; + } + //delete the entry from snatIntIpportinfo + try { + removeFromSnatIpPortDS(segmentId, address.getIpAddress()); + } catch (Exception e){ + LOG.error("NAPT Service : releaseAddressMapping failed, Removal of snatipportmap {} for router {} failed {}" , address.getIpAddress(), segmentId, e); + return false; + } + } else { + LOG.error("NAPT Service : releaseAddressMapping failed, segmentId {} and internalIpPort {} not found in IpMap DS", segmentId, internalIpPort); + return false; + } + // Finally release port from idmanager + removePortFromPool(internalIpPort, existingIpPort.getIpAddress()); + + LOG.debug("NAPT Service : Exit of releaseAddressMapping successfully for segmentId {} and internalIpPort {}", segmentId, internalIpPort); + return true; + + } + + protected void releaseIpExtPortMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) { + String internalIpPort = address.getIpAddress() + ":" + address.getPortNumber(); + SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol); + if(existingIpPort != null) { + // delete the entry from IpPortMap DS + try { + removeFromIpPortMapDS(segmentId, internalIpPort , protocol); + // Finally release port from idmanager + removePortFromPool(internalIpPort, existingIpPort.getIpAddress()); + } catch (Exception e){ + LOG.error("NAPT Service : releaseAddressMapping failed, Removal of ipportmap {} for router {} failed {}" , + internalIpPort, segmentId, e); + } + } else { + LOG.error("NAPT Service : releaseIpExtPortMapping failed, segmentId {} and internalIpPort {} not found in IpPortMap DS", segmentId, internalIpPort); + } + + //delete the entry of port for InternalIp from snatIntIpportMappingDS + try { + removeSnatIntIpPortDS(segmentId,address, protocol); + } catch (Exception e){ + LOG.error("NAPT Service : releaseSnatIpPortMapping failed, Removal of snatipportmap {} for router {} failed {}" , + address.getIpAddress(), segmentId, e); + } + } + + /** + * removes the internal ip to external ip mapping if present + * @param segmentId + * @return true if successfully removed + */ + public boolean removeMapping(long segmentId) { + try { + removeIpMappingForRouterID(segmentId); + } catch (Exception e){ + LOG.error("NAPT Service : Removal of IPMapping for router {} failed {}" , segmentId, e); + return false; + } + + //TODO : This is when router is deleted then cleanup the entries in tables, ports etc - Delete scenarios + return false; + } + + // 2. Utility functions + protected InstanceIdentifier getIpMapIdentifier(long segid, String internal) { + InstanceIdentifier id = InstanceIdentifier.builder( + IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segid)).child(IpMap.class, new IpMapKey(internal)).build(); + return id; + } + + public static List getIpMapList(DataBroker broker, Long routerId) { + InstanceIdentifier id = getIpMapList(routerId); + Optional ipMappingListData = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id); + if (ipMappingListData.isPresent()) { + IpMapping ipMapping = ipMappingListData.get(); + return ipMapping.getIpMap(); + } + return null; + } + + protected static InstanceIdentifier getIpMapList(long routerId) { + InstanceIdentifier id = InstanceIdentifier.builder( + IntextIpMap.class).child(IpMapping.class, new IpMappingKey(routerId)).build(); + return id; + } + + protected InstanceIdentifier getIpPortMapIdentifier(long segid, String internal, NAPTEntryEvent.Protocol protocol) { + ProtocolTypes protocolType = NatUtil.getProtocolType(protocol); + InstanceIdentifier id = InstanceIdentifier.builder( + IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(segid)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType)). + child(IpPortMap.class, new IpPortMapKey(internal)).build(); + return id; + } + + protected SessionAddress checkIpPortMap(long segmentId, String internalIpPort, NAPTEntryEvent.Protocol protocol) { + + LOG.debug("NAPT Service : checkIpPortMap called with segmentId {} and internalIpPort {}", segmentId, internalIpPort); + ProtocolTypes protocolType = NatUtil.getProtocolType(protocol); + // check if ip-port-map node is there + InstanceIdentifierBuilder idBuilder = + InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(segmentId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType)); + InstanceIdentifier id = idBuilder.build(); + Optional intextIpProtocolType = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id); + if (intextIpProtocolType.isPresent()) { + List ipPortMaps = intextIpProtocolType.get().getIpPortMap(); + for (IpPortMap ipPortMap : ipPortMaps) { + if (ipPortMap.getIpPortInternal().equals(internalIpPort)) { + LOG.debug("NAPT Service : IpPortMap : {}", ipPortMap); + SessionAddress externalIpPort = new SessionAddress(ipPortMap.getIpPortExternal().getIpAddress(), + ipPortMap.getIpPortExternal().getPortNum()); + LOG.debug("NAPT Service : checkIpPortMap returning successfully externalIP {} and port {}", + externalIpPort.getIpAddress(), externalIpPort.getPortNumber()); + return externalIpPort; + } + } + } + // return null if not found + LOG.error("NAPT Service : no-entry in checkIpPortMap, returning NULL [should be OK] for segmentId {} and internalIPPort {}", segmentId, internalIpPort); + return null; + } + + protected String checkIpMap(long segmentId, String internalIp) { + + LOG.debug("NAPT Service : checkIpMap called with segmentId {} and internalIp {}", segmentId, internalIp); + String externalIp; + // check if ip-map node is there + InstanceIdentifierBuilder idBuilder = + InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segmentId)); + InstanceIdentifier id = idBuilder.build(); + Optional ipMapping = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id); + if (ipMapping.isPresent()) { + List ipMaps = ipMapping.get().getIpMap(); + for (IpMap ipMap : ipMaps) { + if (ipMap.getInternalIp().equals(internalIp)) { + LOG.debug("NAPT Service : IpMap : {}", ipMap); + externalIp = ipMap.getExternalIp().toString(); + LOG.debug("NAPT Service : checkIpMap successfully returning externalIp {}", externalIp ); + return externalIp; + } else if (ipMap.getInternalIp().contains("/")) { // subnet case + SubnetUtils subnetUtils = new SubnetUtils(ipMap.getInternalIp()); + SubnetInfo subnetInfo = subnetUtils.getInfo(); + if (subnetInfo.isInRange(internalIp)) { + LOG.debug("NAPT Service : internalIp {} found to be IpMap of internalIpSubnet {}", internalIp, ipMap.getInternalIp()); + externalIp = ipMap.getExternalIp().toString(); + LOG.debug("NAPT Service : checkIpMap successfully returning externalIp {}", externalIp ); + return externalIp; + } + } + } + } + // return null if not found + LOG.error("NAPT Service : checkIpMap failed, returning NULL for segmentId {} and internalIp {}", segmentId, internalIp); + return null; + } + + protected void removeSnatIntIpPortDS(long segmentId, SessionAddress address,NAPTEntryEvent.Protocol protocol) { + LOG.trace("NAPT Service : removeSnatIntIpPortDS method called for IntIpport {} of router {} ",address,segmentId); + ProtocolTypes protocolType = NatUtil.getProtocolType(protocol); + List portList = NatUtil.getInternalIpPortListInfo(broker,segmentId,address.getIpAddress(),protocolType); + if (portList == null || portList.isEmpty() || !portList.contains(address.getPortNumber())) { + LOG.debug("Internal IP {} for port {} entry not found in SnatIntIpPort DS",address.getIpAddress(),address.getPortNumber()); + return; + } + LOG.trace("NAPT Service : PortList {} retrieved for InternalIp {} of router {}",portList,address.getIpAddress(),segmentId); + Integer port = address.getPortNumber(); + portList.remove(port); + + IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder(); + IntIpProtoType intIpProtocolType = builder.setKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build(); + try { + MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, NatUtil.buildSnatIntIpPortIdentifier(segmentId, address.getIpAddress(), protocolType), intIpProtocolType); + } catch (Exception ex) { + LOG.error("NAPT Service : Failed to write into snat-internal-ip-port-info with exception {}", ex.getMessage() ); + } + LOG.debug("NAPT Service : Removing SnatIp {} Port {} of router {} from SNATIntIpport datastore : {}" + ,address.getIpAddress(),address.getPortNumber(),segmentId); + } + + protected void removeFromSnatIpPortDS(long segmentId, String internalIp) { + InstanceIdentifier intIp = InstanceIdentifier.builder(SnatintIpPortMap.class).child + (IntipPortMap.class, new IntipPortMapKey(segmentId)).child(IpPort.class, new IpPortKey(internalIp)).build(); + // remove from SnatIpPortDS + LOG.debug("NAPT Service : Removing SnatIpPort from datastore : {}", intIp); + MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, intIp); + + } + + protected void removeFromIpPortMapDS(long segmentId, String internalIpPort, NAPTEntryEvent.Protocol protocol) { + ProtocolTypes protocolType = NatUtil.getProtocolType(protocol); + InstanceIdentifierBuilder idBuilder = InstanceIdentifier.builder(IntextIpPortMap.class) + .child(IpPortMapping.class, new IpPortMappingKey(segmentId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType)) + .child(IpPortMap.class, new IpPortMapKey(internalIpPort)); + InstanceIdentifier id = idBuilder.build(); + // remove from ipportmap DS + LOG.debug("NAPT Service : Removing ipportmap from datastore : {}", id); + MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, id); + } + + protected void removeFromIpMapDS(long segmentId, String internalIp) { + InstanceIdentifierBuilder idBuilder = InstanceIdentifier.builder(IntextIpMap.class) + .child(IpMapping.class, new IpMappingKey(segmentId)) + .child(IpMap.class, new IpMapKey(internalIp)); + InstanceIdentifier id = idBuilder.build(); + // remove from ipmap DS + LOG.debug("NAPT Service : Removing ipmap from datastore : {}", id); + MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id); + } + + private void removeIpMappingForRouterID(long segmentId) { + InstanceIdentifierBuilder idBuilder = InstanceIdentifier.builder(IntextIpMap.class) + .child(IpMapping.class, new IpMappingKey(segmentId)); + InstanceIdentifier id = idBuilder.build(); + // remove from ipmap DS + LOG.debug("NAPT Service : Removing ipmap from datastore : {}", id); + MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id); + } + + protected void removePortFromPool(String internalIpPort, String externalIp) { + LOG.debug("NAPT Service : removePortFromPool method called"); + ReleaseIdInput idInput = new ReleaseIdInputBuilder(). + setPoolName(externalIp) + .setIdKey(internalIpPort).build(); + try { + Future> result = idManager.releaseId(idInput); + RpcResult rpcResult = result.get(); + if(!rpcResult.isSuccessful()) { + LOG.error("NAPT Service : idmanager failed to remove port from pool {}", rpcResult.getErrors()); + } + LOG.debug("NAPT Service : Removed port from pool for InternalIpPort {} with externalIp {}",internalIpPort,externalIp); + } catch (InterruptedException | ExecutionException e) { + LOG.error("NAPT Service : idmanager failed with Exception {} when removing entry in pool with key {}, ", e, internalIpPort); + } + } +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptPacketInHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptPacketInHandler.java new file mode 100644 index 00000000..b663f506 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptPacketInHandler.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.controller.liblldp.NetUtils; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.NWUtil; +import org.opendaylight.vpnservice.mdsalutil.packet.Ethernet; +import org.opendaylight.vpnservice.mdsalutil.packet.IPv4; +import org.opendaylight.vpnservice.mdsalutil.packet.TCP; +import org.opendaylight.vpnservice.mdsalutil.packet.UDP; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.util.HashSet; +import com.google.common.primitives.Ints; + +public class NaptPacketInHandler implements PacketProcessingListener { + + private static final Logger LOG = LoggerFactory.getLogger(NaptPacketInHandler.class); + private final static HashSet incomingPacketMap = new HashSet<>(); + private final EventDispatcher naptEventdispatcher; + + public NaptPacketInHandler(EventDispatcher eventDispatcher) { + this.naptEventdispatcher = eventDispatcher; + } + + @Override + public void onPacketReceived(PacketReceived packetReceived) { + String internalIPAddress = ""; + int portNumber = 0; + long routerId = 0L; + NAPTEntryEvent.Operation operation = NAPTEntryEvent.Operation.ADD; + NAPTEntryEvent.Protocol protocol; + + Short tableId = packetReceived.getTableId().getValue(); + + if (LOG.isTraceEnabled()) { + LOG.trace("packet: {}, tableId {}", packetReceived, tableId); + } + + if (tableId == NatConstants.OUTBOUND_NAPT_TABLE) { + LOG.debug("NAT Service : NAPTPacketInHandler Packet for Outbound NAPT Table"); + byte[] inPayload = packetReceived.getPayload(); + Ethernet ethPkt = new Ethernet(); + if (inPayload != null) { + try { + ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte); + } catch (Exception e) { + LOG.warn("Failed to decode Packet", e); + return; + } + if (ethPkt.getPayload() instanceof IPv4) { + IPv4 ipPkt = (IPv4) ethPkt.getPayload(); + byte[] ipSrc = Ints.toByteArray(ipPkt.getSourceAddress()); + + internalIPAddress = NatUtil.toStringIpAddress(ipSrc, LOG); + LOG.trace("Retrieved internalIPAddress {}", internalIPAddress); + if (ipPkt.getPayload() instanceof TCP) { + TCP tcpPkt = (TCP) ipPkt.getPayload(); + portNumber = tcpPkt.getSourcePort(); + protocol = NAPTEntryEvent.Protocol.TCP; + LOG.trace("Retrieved TCP portNumber {}", portNumber); + } else if (ipPkt.getPayload() instanceof UDP) { + UDP udpPkt = (UDP) ipPkt.getPayload(); + portNumber = udpPkt.getSourcePort(); + protocol = NAPTEntryEvent.Protocol.UDP; + LOG.trace("Retrieved UDP portNumber {}", portNumber); + } else { + LOG.error("Incoming Packet is neither TCP or UDP packet"); + return; + } + } else { + LOG.error("Incoming Packet is not IPv4 packet"); + return; + } + + if(internalIPAddress != null) { + String sourceIPPortKey = internalIPAddress + ":" + portNumber; + LOG.debug("NAT Service : sourceIPPortKey {} mapping maintained in the map", sourceIPPortKey); + if (!incomingPacketMap.contains(sourceIPPortKey)) { + incomingPacketMap.add(internalIPAddress + portNumber); + + BigInteger metadata = packetReceived.getMatch().getMetadata().getMetadata(); + routerId = (metadata.and(MetaDataUtil.METADATA_MASK_VRFID)).longValue(); + if( routerId <= 0) { + LOG.error("Nat Service : Router ID is invalid"); + return; + } + //send to Event Queue + NAPTEntryEvent naptEntryEvent = new NAPTEntryEvent(internalIPAddress,portNumber,routerId, + operation,protocol); + naptEventdispatcher.addNaptEvent(naptEntryEvent); + } else { + LOG.trace("Ignore the packet, already processed"); + } + }else { + LOG.error("Nullpointer exception in retrieving internalIPAddress"); + } + } + }else { + LOG.trace("Packet is not from the Outbound NAPT table"); + } + } + + public void removeIncomingPacketMap(String sourceIPPortKey) { + incomingPacketMap.remove(sourceIPPortKey); + LOG.debug("NAT Service : sourceIPPortKey {} mapping is removed from map", sourceIPPortKey); + } +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptSwitchHA.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptSwitchHA.java new file mode 100644 index 00000000..66fadc5b --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptSwitchHA.java @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import com.google.common.base.Optional; +import org.opendaylight.bgpmanager.api.IBgpManager; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.*; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase; +import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInputBuilder; +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.*; +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; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap; +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.IpPortMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch; +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.vpn.rpc.rev160201.VpnRpcService; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class NaptSwitchHA { + private static final Logger LOG = LoggerFactory.getLogger(NaptSwitchHA.class); + private final DataBroker dataBroker; + private IMdsalApiManager mdsalManager; + private ItmRpcService itmManager; + private OdlInterfaceRpcService interfaceManager; + private IdManagerService idManager; + private NAPTSwitchSelector naptSwitchSelector; + private ExternalRoutersListener externalRouterListener; + private IBgpManager bgpManager; + private VpnRpcService vpnService; + private FibRpcService fibService; + + public NaptSwitchHA(DataBroker broker,NAPTSwitchSelector selector){ + dataBroker = broker; + naptSwitchSelector = selector; + } + + public void setItmManager(ItmRpcService itmManager) { + this.itmManager = itmManager; + } + + public void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) { + this.interfaceManager = interfaceManager; + } + + public void setIdManager(IdManagerService idManager) { + this.idManager = idManager; + } + + void setExternalRoutersListener(ExternalRoutersListener externalRoutersListener) { + this.externalRouterListener = externalRoutersListener; + } + + public void setBgpManager(IBgpManager bgpManager) { + this.bgpManager = bgpManager; + } + + public void setVpnService(VpnRpcService vpnService) { + this.vpnService = vpnService; + } + + public void setFibService(FibRpcService fibService) { + this.fibService = fibService; + } + + /* This method checks the switch that gone down is a NaptSwitch for a router. + If it is a NaptSwitch + 1) selects new NAPT switch + 2) installs nat flows in new NAPT switch + table 21(FIB)->26(PSNAT)->group(resubmit/napttunnel)->36(Terminating)->46(outbound)->47(resubmit)->21 + 3) modify the group and miss entry flow in other vSwitches pointing to newNaptSwitch + 4) Remove nat flows in oldNaptSwitch + */ + public void handleNaptSwitchDown(BigInteger dpnId){ + + LOG.debug("handleNaptSwitchDown method is called with dpnId {}",dpnId); + BigInteger naptSwitch; + try { + NaptSwitches naptSwitches = NatUtil.getNaptSwitch(dataBroker); + if (naptSwitches == null || naptSwitches.getRouterToNaptSwitch() == null || naptSwitches.getRouterToNaptSwitch().isEmpty()) { + LOG.debug("NaptSwitchDown: NaptSwitch is not allocated for none of the routers"); + return; + } + for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) { + String routerName = routerToNaptSwitch.getRouterName(); + naptSwitch = routerToNaptSwitch.getPrimarySwitchId(); + boolean naptStatus = isNaptSwitchDown(routerName,dpnId,naptSwitch); + if (!naptStatus) { + LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}", + dpnId, routerName); + } else { + removeSnatFlowsInOldNaptSwitch(routerName,naptSwitch); + return; + } + } + } catch (Exception ex) { + LOG.error("Exception in handleNaptSwitchDown method {}",ex); + } + } + + private void removeSnatFlowsInOldNaptSwitch(String routerName, BigInteger naptSwitch) { + //remove SNAT flows in old NAPT SWITCH + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + if (routerId == NatConstants.INVALID_ID) { + LOG.error("Invalid routerId returned for routerName {}",routerName); + return; + } + BigInteger cookieSnatFlow = NatUtil.getCookieSnatFlow(routerId); + + //Build and remove flows in outbound NAPT table + try { + FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow); + mdsalManager.removeFlow(outboundNaptFlowEntity); + LOG.info("Removed all flows for router {} in the table {} for oldNaptswitch {}" + ,routerName, NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch); + } catch (Exception ex) { + LOG.info("Failed to remove all flows for router {} in the table {} for oldNaptswitch {}" + ,routerName, NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch); + } + + //Build and remove flows in inbound NAPT table + try { + FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.INBOUND_NAPT_TABLE, + cookieSnatFlow); + mdsalManager.removeFlow(inboundNaptFlowEntity); + LOG.info("Removed all flows for router {} in the table {} for oldNaptswitch {}" + ,routerName, NatConstants.INBOUND_NAPT_TABLE, naptSwitch); + } catch (Exception ex) { + LOG.info("Failed to remove all flows for router {} in the table {} for oldNaptswitch {}" + ,routerName, NatConstants.INBOUND_NAPT_TABLE, naptSwitch); + } + + //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table + String tsFlowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, routerId); + FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef); + + LOG.info("Remove the flow in table {} for the active switch with the DPN ID {} and router ID {}" + ,NatConstants.TERMINATING_SERVICE_TABLE, naptSwitch, routerId); + mdsalManager.removeFlow(tsNatFlowEntity); + + //Remove the Outbound flow entry which forwards the packet to Outbound NAPT Table + String outboundNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, routerId); + FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch, + NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef); + LOG.info("Remove the flow in the for the active switch with the DPN ID {} and router ID {}" + ,NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId); + mdsalManager.removeFlow(outboundNatFlowEntity); + + //Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table + String naptPFibflowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.NAPT_PFIB_TABLE, routerId); + FlowEntity naptPFibFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.NAPT_PFIB_TABLE,naptPFibflowRef); + LOG.info("Remove the flow in the for the active switch with the DPN ID {} and router ID {}", + NatConstants.NAPT_PFIB_TABLE, naptSwitch, routerId); + mdsalManager.removeFlow(naptPFibFlowEntity); + + //Remove Fib entries and 36-> 44 + Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId); + if (networkId == null) { + LOG.debug("network is not associated to router {}", routerId); + } + Optional routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, + NatUtil.buildRouterIdentifier(routerName)); + if(routerData.isPresent()){ + List externalIps = routerData.get().getExternalIps(); + if (externalIps != null) { + externalRouterListener.advToBgpAndRemoveFibAndTsFlows(naptSwitch, routerId, networkId, externalIps); + LOG.debug("Successfully removed fib entries in naptswitch {} for router {} with external IP {}", naptSwitch, + routerId, externalIps); + } else { + LOG.debug("ExternalIps not found for router {} with networkId {}",routerName,networkId); + } + } + } + + public boolean isNaptSwitchDown(String routerName, BigInteger dpnId , BigInteger naptSwitch) { + if (!naptSwitch.equals(dpnId)) { + LOG.debug("DpnId {} is not a naptSwitch {} for Router {}",dpnId, naptSwitch, routerName); + return false; + } + LOG.debug("NaptSwitch {} is down for Router {}", naptSwitch, routerName); + //elect a new NaptSwitch + naptSwitch = naptSwitchSelector.selectNewNAPTSwitch(routerName); + if (naptSwitch.equals("0")) { + LOG.info("No napt switch is elected since all the switches for router {} are down",routerName); + return true; + } + //checking elected switch health status + if (!getSwitchStatus(naptSwitch)) { + LOG.error("Newly elected Napt switch {} for router {} is down", naptSwitch, routerName); + return true; + } + LOG.debug("New NaptSwitch {} is up for Router {} and can proceed for flow installation",naptSwitch, routerName); + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + if (routerId == NatConstants.INVALID_ID) { + LOG.error("Invalid routerId returned for routerName {}", routerName); + return true; + } + //update napt model for new napt switch + boolean naptUpdated = updateNaptSwitch(routerName, naptSwitch); + if (naptUpdated) { + //update group of naptswitch point to table36/ordinary switch point to naptswitchtunnelport + updateNaptSwitchBucketStatus(routerName, naptSwitch); + } else { + LOG.error("Failed to update naptSwitch model for newNaptSwitch {} for router {}",naptSwitch, routerName); + } + //36 -> 46 ..Install flow going to 46 from table36 + externalRouterListener.installTerminatingServiceTblEntry(naptSwitch, routerName); + + //Install default flows punting to controller in table 46(OutBoundNapt table) + externalRouterListener.installOutboundMissEntry(routerName, naptSwitch); + + //Table 47 point to table 21 for inbound traffic + LOG.debug("installNaptPfibEntry for dpnId {} and routerId {}", naptSwitch, routerId); + externalRouterListener.installNaptPfibEntry(naptSwitch, routerId); + + //Table 47 point to table 21 for outbound traffic + String vpnName = getVpnName(routerId); + if(vpnName != null) { + long vpnId = NatUtil.getVpnId(dataBroker, vpnName); + if(vpnId > 0) { + LOG.debug("installNaptPfibEntry for dpnId {} and vpnId {}", naptSwitch, vpnId); + externalRouterListener.installNaptPfibEntry(naptSwitch, vpnId); + } else { + LOG.debug("Associated vpnId not found for router {}",routerId); + } + } else { + LOG.debug("Associated vpnName not found for router {}",routerId); + } + + //Install Fib entries for ExternalIps & program 36 -> 44 + + Optional ipMappingOptional = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, + getIpMappingBuilder(routerId)); + if (vpnName != null) { + if (ipMappingOptional.isPresent()) { + List ipMaps = ipMappingOptional.get().getIpMap(); + for (IpMap ipMap : ipMaps) { + String externalIp = ipMap.getExternalIp(); + LOG.debug("advToBgpAndInstallFibAndTsFlows for naptswitch {}, vpnName {} and externalIp {}", + naptSwitch, vpnName, externalIp); + externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitch, NatConstants.INBOUND_NAPT_TABLE, + vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG); + LOG.debug("Successfully added fib entries in naptswitch {} for router {} with external IP {}", naptSwitch, + routerId, externalIp); + } + } + } else { + LOG.debug("Vpn is not associated to the network of router {}",routerName); + } + + boolean flowInstalledStatus = handleFlowsInNewNaptSwitch(routerId, dpnId, naptSwitch); + if (flowInstalledStatus) { + LOG.debug("Installed all activesession flows in newNaptSwitch {} for routerName {}", routerName); + } else { + LOG.error("Failed to install flows in newNaptSwitch {} for routerId {}", naptSwitch, routerId); + } + return true; + } + + private InstanceIdentifier getIpMappingBuilder(Long routerId) { + InstanceIdentifier idBuilder = InstanceIdentifier.builder(IntextIpMap.class) + .child(IpMapping.class, new IpMappingKey(routerId)).build(); + return idBuilder; + } + + private String getVpnName(long routerId) { + Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId); + if(networkId == null) { + LOG.error("networkId is null for the router ID {}", routerId); + } else { + final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG); + if (vpnName != null) { + LOG.debug("retreived vpnname {} associated with ext nw {} in router {}", + vpnName,networkId,routerId); + return vpnName; + } else { + LOG.error("No VPN associated with ext nw {} belonging to routerId {}", + networkId, routerId); + } + } + return null; + } + + public void updateNaptSwitchBucketStatus(String routerName, BigInteger naptSwitch) { + LOG.debug("updateNaptSwitchBucketStatus method is called"); + + List dpnList = getDpnListForRouter(routerName); + for (BigInteger dpn : dpnList) { + if (dpn.equals(naptSwitch)) { + LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is naptSwitch for router {}",dpn,routerName); + List bucketInfoList = handleGroupInPrimarySwitch(); + modifySnatGroupEntry(naptSwitch, bucketInfoList, routerName); + } else { + LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is not naptSwitch for router {}" + , dpn, routerName); + List bucketInfoList = handleGroupInNeighborSwitches(dpn, routerName, naptSwitch); + if (bucketInfoList == null) { + LOG.debug("bucketInfo is not populated for orinaryswitch {} whose naptSwitch {} with router {} ", + dpn,routerName,naptSwitch); + return; + } + modifySnatGroupEntry(naptSwitch, bucketInfoList, routerName); + } + } + } + + private boolean handleFlowsInNewNaptSwitch(Long routerId,BigInteger oldNaptSwitch, BigInteger newNaptSwitch) { + + LOG.debug("Proceeding to install flows in newNaptSwitch {} for routerId {}", routerId); + IpPortMapping ipPortMapping = getIpPortMapping(routerId); + if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null || ipPortMapping.getIntextIpProtocolType().isEmpty()) { + LOG.debug("No Internal Ip Port mapping associated to router {}, no flows need to be installed in" + + "newNaptSwitch ", routerId, newNaptSwitch); + return true; + } + //getvpnId + Long vpnId = null; + try { + vpnId = getVpnIdForRouter(routerId); + }catch (Exception ex) { + LOG.error("Failed to retreive vpnID for router {} : {}", routerId,ex); + return false; + } + for (IntextIpProtocolType protocolType : ipPortMapping.getIntextIpProtocolType()) { + if (protocolType.getIpPortMap() == null || protocolType.getIpPortMap().isEmpty()) { + LOG.debug("No {} session associated to router {}", protocolType.getProtocol(), routerId); + return true; + } + for (IpPortMap intIpPortMap : protocolType.getIpPortMap()) { + String internalIpAddress = intIpPortMap.getIpPortInternal().split(":")[0]; + String intportnum = intIpPortMap.getIpPortInternal().split(":")[1]; + + //Get the external IP address and the port from the model + NAPTEntryEvent.Protocol proto = protocolType.getProtocol().toString().equals(ProtocolTypes.TCP.toString()) + ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP; + IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId, + internalIpAddress, intportnum, proto); + if (ipPortExternal == null) { + LOG.debug("External Ipport mapping is not found for internalIp {} with port {}", internalIpAddress, intportnum); + continue; + } + String externalIpAddress = ipPortExternal.getIpAddress(); + Integer extportNumber = ipPortExternal.getPortNum(); + LOG.debug("ExternalIPport {}:{} mapping for internal ipport {}:{}",externalIpAddress,extportNumber, + internalIpAddress,intportnum); + + SessionAddress sourceAddress = new SessionAddress(internalIpAddress,Integer.valueOf(intportnum)); + SessionAddress externalAddress = new SessionAddress(externalIpAddress,extportNumber); + + //checking naptSwitch status before installing flows + if(getSwitchStatus(newNaptSwitch)) { + //Install the flow in newNaptSwitch Outbound NAPT table. + try { + NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, + vpnId, routerId, sourceAddress, externalAddress, proto); + } catch (Exception ex) { + LOG.error("Failed to add flow in OUTBOUND_NAPT_TABLE for routerid {} dpnId {} ipport {}:{} proto {}" + + "extIpport {}:{}", routerId, newNaptSwitch, internalIpAddress + , intportnum, proto, externalAddress, extportNumber); + return false; + } + LOG.debug("Succesfully installed a flow in SecondarySwitch {} Outbound NAPT table for router {} " + + "ipport {}:{} proto {} extIpport {}:{}", newNaptSwitch,routerId, internalIpAddress + , intportnum, proto, externalAddress, extportNumber); + //Install the flow in newNaptSwitch Inbound NAPT table. + try { + NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.INBOUND_NAPT_TABLE, + vpnId, routerId, externalAddress, sourceAddress, proto); + } catch (Exception ex) { + LOG.error("Failed to add flow in INBOUND_NAPT_TABLE for routerid {} dpnId {} extIpport{}:{} proto {} ipport {}:{}", + routerId, newNaptSwitch, externalAddress, extportNumber, + proto, internalIpAddress, intportnum); + return false; + } + LOG.debug("Succesfully installed a flow in SecondarySwitch {} Inbound NAPT table for router {} " + + "ipport {}:{} proto {} extIpport {}:{}", newNaptSwitch,routerId, internalIpAddress + , intportnum, proto, externalAddress, extportNumber); + + } else { + LOG.error("NewNaptSwitch {} gone down while installing flows from oldNaptswitch {}", + newNaptSwitch,oldNaptSwitch); + return false; + } + } + } + return true; + } + + private Long getVpnIdForRouter(Long routerId) { + try { + //getvpnId + Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId); + if (networkId == null) { + LOG.debug("network is not associated to router {}", routerId); + } else { + Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId); + if (vpnUuid == null) { + LOG.debug("vpn is not associated for network {} in router {}", networkId, routerId); + } else { + Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue()); + if (vpnId != null) { + LOG.debug("retrieved vpnId {} for router {}",vpnId,routerId); + return vpnId; + } else { + LOG.debug("retrieved invalid vpn Id"); + } + } + } + } catch (Exception ex){ + LOG.debug("Exception while retreiving vpnId for router {} - {}", routerId, ex); + } + return null; + } + + private List getDpnListForRouter(String routerName) { + List dpnList = new ArrayList(); + List vpnDpnList = NatUtil.getVpnToDpnList(dataBroker, routerName); + for (VpnToDpnList vpnToDpn : vpnDpnList) { + dpnList.add(vpnToDpn.getDpnId()); + } + return dpnList; + } + + public boolean getSwitchStatus(BigInteger switchId){ + NodeId nodeId = new NodeId("openflow:" + switchId); + LOG.debug("Querying switch with dpnId {} is up/down", nodeId); + InstanceIdentifier nodeInstanceId = InstanceIdentifier.builder(Nodes.class) + .child(Node.class, new NodeKey(nodeId)).build(); + Optional nodeOptional = NatUtil.read(dataBroker,LogicalDatastoreType.OPERATIONAL,nodeInstanceId); + if (nodeOptional.isPresent()) { + LOG.debug("Switch {} is up", nodeId); + return true; + } + LOG.debug("Switch {} is down", nodeId); + return false; + } + + public List handleGroupInPrimarySwitch() { + 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(bucketPrimary); + return listBucketInfo; + } + + public List handleGroupInNeighborSwitches(BigInteger dpnId, String routerName, BigInteger naptSwitch) { + List listBucketInfo = new ArrayList(); + String ifNamePrimary; + Long routerId = NatUtil.getVpnId(dataBroker, routerName); + if (routerId == NatConstants.INVALID_ID) { + LOG.error("Invalid routerId returned for routerName {}",routerName); + return listBucketInfo; + } + ifNamePrimary = getTunnelInterfaceName(dpnId, naptSwitch); + if (ifNamePrimary != null) { + LOG.debug("TunnelInterface {} between ordinary switch {} and naptSwitch {}",ifNamePrimary,dpnId,naptSwitch); + List listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId); + BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary); + listBucketInfo.add(bucketPrimary); + } else { + LOG.debug("No TunnelInterface between ordinary switch {} and naptSwitch {}",dpnId,naptSwitch); + } + return listBucketInfo; + } + + protected void installSnatGroupEntry(BigInteger dpnId, List bucketInfo, String routerName) { + GroupEntity groupEntity = null; + try { + long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager); + LOG.debug("install SnatMissEntry for groupId {} for dpnId {} for router {}", groupId, dpnId,routerName); + groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, + GroupTypes.GroupAll, bucketInfo); + mdsalManager.installGroup(groupEntity); + LOG.debug("installed the SNAT to NAPT GroupEntity:{}", groupEntity); + } catch (Exception ex) { + LOG.error("Failed to install group for groupEntity {} : {}",groupEntity,ex); + } + } + + private void modifySnatGroupEntry(BigInteger dpnId, List bucketInfo, String routerName) { + installSnatGroupEntry(dpnId,bucketInfo,routerName); + LOG.debug("modified SnatMissEntry for dpnId {} of router {}",dpnId,routerName); + } + + protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) { + try { + Future> result = itmManager.getTunnelInterfaceName( + new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId).setDestinationDpid(dstDpId).build()); + RpcResult rpcResult = result.get(); + if(!rpcResult.isSuccessful()) { + LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors()); + } else { + return rpcResult.getResult().getInterfaceName(); + } + } catch (InterruptedException | ExecutionException e) { + LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {} : {}", + srcDpId, dstDpId, e); + } + + return null; + } + + protected List getEgressActionsForInterface(String ifName, long routerId) { + LOG.debug("getEgressActionsForInterface called for interface {}", ifName); + List listActionInfo = new ArrayList(); + try { + Future> result = + interfaceManager.getEgressActionsForInterface( + new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build()); + RpcResult rpcResult = result.get(); + if(!rpcResult.isSuccessful()) { + LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}" + , ifName, rpcResult.getErrors()); + } else { + List actions = + rpcResult.getResult().getAction(); + for (Action action : actions) { + org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction(); + if (actionClass instanceof OutputActionCase) { + listActionInfo.add(new ActionInfo(ActionType.output, + new String[] {((OutputActionCase)actionClass).getOutputAction() + .getOutputNodeConnector().getValue()})); + } else if (actionClass instanceof PushVlanActionCase) { + listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {})); + } else if (actionClass instanceof SetFieldCase) { + if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) { + int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch() + .getVlanId().getVlanId().getValue(); + listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid, + new String[] { Long.toString(vlanVid) })); + } + } + } + } + } catch (InterruptedException | ExecutionException e) { + LOG.warn("Exception when egress actions for interface {}", ifName, e); + } + return listActionInfo; + } + + private IpPortMapping getIpPortMapping(Long routerId) { + Optional ipPortMapData = NatUtil.read(this.dataBroker, LogicalDatastoreType.CONFIGURATION, + buildIpToPortMapIdentifier(routerId)); + if (ipPortMapData.isPresent()) { + return ipPortMapData.get(); + } + return null; + } + + public boolean updateNaptSwitch(String routerName, BigInteger naptSwitchId) { + RouterToNaptSwitch naptSwitch = new RouterToNaptSwitchBuilder().setKey(new RouterToNaptSwitchKey(routerName)) + .setPrimarySwitchId(naptSwitchId).build(); + try { + MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, + NatUtil.buildNaptSwitchRouterIdentifier(routerName), naptSwitch); + } catch (Exception ex) { + LOG.error("Failed to write naptSwitch {} for router {} in ds", + naptSwitchId,routerName); + return false; + } + LOG.debug("Successfully updated naptSwitch {} for router {} in ds", + naptSwitchId,routerName); + return true; + } + + private InstanceIdentifier buildIpToPortMapIdentifier(Long routerId) { + InstanceIdentifier ipPortMapId = InstanceIdentifier.builder(IntextIpPortMap.class).child + (IpPortMapping.class, new IpPortMappingKey(routerId)).build(); + return ipPortMapId; + } + + public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId, int addordel) { + + FlowEntity flowEntity = null; + long routerId = NatUtil.getVpnId(dataBroker, routerName); + if (routerId == NatConstants.INVALID_ID) { + LOG.error("Invalid routerId returned for routerName {}",routerName); + return flowEntity; + } + 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 })); + + String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName); + + if (addordel == NatConstants.ADD_FLOW) { + List instructions = new ArrayList(); + List actionsInfo = new ArrayList(); + + ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] { + BigInteger.valueOf(routerId)}) ; + actionsInfo.add(actionSetField); + LOG.debug("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)); + + flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef, + NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_SNAT_TABLE, matches, instructions); + } else { + flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef, + NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_SNAT_TABLE, matches, null); + } + return flowEntity; + } + + private String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) { + return new StringBuilder().append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR). + append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString(); + } +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatConstants.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatConstants.java new file mode 100644 index 00000000..bf4a8e24 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatConstants.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; + + +public class NatConstants { + public static final short INBOUND_NAPT_TABLE = 44; + public static final short OUTBOUND_NAPT_TABLE = 46; + public static final short NAPT_PFIB_TABLE = 47; + public static final short TERMINATING_SERVICE_TABLE = 36; + public static final short DEFAULT_NAPT_FLOW_PRIORITY = 10; + public static final String NAPT_FLOW_NAME = "SNAT"; + public static BigInteger COOKIE_NAPT_BASE = new BigInteger("8000000", 16); + public static final String NAPT_FLOWID_PREFIX = "SNAT."; + public static final String FLOWID_SEPARATOR = "."; + public static final int DEFAULT_NAPT_IDLE_TIMEOUT = 300; + public static int EVENT_QUEUE_LENGTH = 1000000; + public static final short PDNAT_TABLE = 25; + public static final short DNAT_TABLE = 27; + public static final short SNAT_TABLE = 28; + public static final short PSNAT_TABLE = 26; + public static final short L3_FIB_TABLE = 21; + public static final String FLOWID_PREFIX = "L3."; + public static final int DEFAULT_DNAT_FLOW_PRIORITY = 10; + public static final BigInteger COOKIE_DNAT_TABLE = new BigInteger("8000004", 16); + public static final long INVALID_ID = -1; + public static final BigInteger COOKIE_OUTBOUND_NAPT_TABLE = new BigInteger("8000008", 16); + public static final short DEFAULT_SNAT_FLOW_PRIORITY = 10; + public static final short DEFAULT_PSNAT_FLOW_PRIORITY = 5; + public static final String SNAT_FLOW_NAME = "SNAT"; + public static final String SNAT_FLOWID_PREFIX = "SNAT."; + public static final BigInteger COOKIE_SNAT_TABLE = new BigInteger("8000006", 16); + public static final String SNAT_IDPOOL_NAME = "snatGroupIdPool"; + public static final long SNAT_ID_LOW_VALUE = 200000L; + public static final long SNAT_ID_HIGH_VALUE = 225000L; + public static final int DEFAULT_TS_FLOW_PRIORITY = 10; + public static final BigInteger COOKIE_TS_TABLE = new BigInteger("8000002", 16); + public static final short DEFAULT_PREFIX = 32; + + // Flow Actions + public static final int ADD_FLOW = 0; + public static final int DEL_FLOW = 1; + +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatNodeEventListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatNodeEventListener.java new file mode 100644 index 00000000..79c8f155 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatNodeEventListener.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; + +public class NatNodeEventListener extends AbstractDataChangeListener implements AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(NatNodeEventListener.class); + private ListenerRegistration listenerRegistration; + private NaptSwitchHA naptSwitchHA; + + public NatNodeEventListener(final DataBroker db,final NaptSwitchHA napt) { + super(Node.class); + naptSwitchHA = napt; + registerListener(db); + } + + private void registerListener(final DataBroker db) { + try { + listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, + getWildCardPath(), NatNodeEventListener.this, AsyncDataBroker.DataChangeScope.SUBTREE); + } catch (final Exception e) { + LOG.error("NatNodeEventListener: DataChange listener registration fail!", e); + throw new IllegalStateException("NatNodeEventListener: registration Listener failed.", e); + } + } + + private InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(Nodes.class).child(Node.class); + } + + @Override + protected void remove(InstanceIdentifier identifier, Node del) { + LOG.debug("NatNodeEventListener: Node removed received"); + NodeId nodeId = del.getId(); + String[] node = nodeId.getValue().split(":"); + if(node.length < 2) { + LOG.warn("Unexpected nodeId {}", nodeId.getValue()); + return; + } + BigInteger dpnId = new BigInteger(node[1]); + LOG.debug("NodeId removed is {}",dpnId); + naptSwitchHA.handleNaptSwitchDown(dpnId); + } + + @Override + protected void update(InstanceIdentifier identifier, Node original, Node update) { + LOG.trace("NatNodeEventListener: Node update received"); + } + + @Override + protected void add(InstanceIdentifier identifier, Node add) { + LOG.debug("NatNodeEventListener: Node added received"); + NodeId nodeId = add.getId(); + String[] node = nodeId.getValue().split(":"); + if(node.length < 2) { + LOG.warn("Unexpected nodeId {}", nodeId.getValue()); + return; + } + BigInteger dpnId = new BigInteger(node[1]); + LOG.debug("NodeId added is {}",dpnId); + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + LOG.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + LOG.info("NatNodeEventListener Closed"); + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatServiceProvider.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatServiceProvider.java new file mode 100644 index 00000000..1af22378 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatServiceProvider.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.bgpmanager.api.IBgpManager; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; +import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.md.sal.binding.api.NotificationService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.NeutronvpnService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +public class NatServiceProvider implements BindingAwareProvider, AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(NatServiceProvider.class); + private IMdsalApiManager mdsalManager; + private RpcProviderRegistry rpcProviderRegistry; + private OdlInterfaceRpcService interfaceManager; + private NotificationService notificationService; + private ItmRpcService itmManager; + private FloatingIPListener floatingIpListener; + private ExternalNetworkListener extNwListener; + private NaptManager naptManager; + private NaptEventHandler naptEventHandler; + private BlockingQueue naptEventQueue; + private ExternalNetworksChangeListener externalNetworksChangeListener; + private ExternalRoutersListener externalRouterListener; + private NaptPacketInHandler naptPacketInHandler; + private EventDispatcher eventDispatcher; + private IBgpManager bgpManager; + private NaptFlowRemovedEventHandler naptFlowRemovedEventHandler; + private InterfaceStateEventListener interfaceStateEventListener; + private NatNodeEventListener natNodeEventListener; + private NAPTSwitchSelector naptSwitchSelector; + private RouterPortsListener routerPortsListener; + + public NatServiceProvider(RpcProviderRegistry rpcProviderRegistry) { + this.rpcProviderRegistry = rpcProviderRegistry; + } + + public void setNotificationService(NotificationService notificationService) { + this.notificationService = notificationService; + } + + public void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + public void setBgpManager(IBgpManager bgpManager) { + LOG.debug("BGP Manager reference initialized"); + this.bgpManager = bgpManager; + } + + @Override + public void close() throws Exception { + floatingIpListener.close(); + extNwListener.close(); + externalNetworksChangeListener.close(); + externalRouterListener.close(); + routerPortsListener.close(); + } + + @Override + public void onSessionInitiated(ProviderContext session) { + LOG.info("NAT Manager Provider Session Initiated"); + try { + //Get the DataBroker, PacketProcessingService, IdManagerService and the OdlInterfaceRpcService instances + final DataBroker dataBroker = session.getSALService(DataBroker.class); + PacketProcessingService pktProcessingService = session.getRpcService(PacketProcessingService.class); + IdManagerService idManager = rpcProviderRegistry.getRpcService(IdManagerService.class); + OdlInterfaceRpcService interfaceService = rpcProviderRegistry.getRpcService(OdlInterfaceRpcService.class); + NeutronvpnService neutronvpnService = rpcProviderRegistry.getRpcService(NeutronvpnService.class); + itmManager = rpcProviderRegistry.getRpcService(ItmRpcService.class); + + //Instantiate FloatingIPListener and set the MdsalManager and OdlInterfaceRpcService in it. + floatingIpListener = new FloatingIPListener(dataBroker); + floatingIpListener.setInterfaceManager(interfaceService); + floatingIpListener.setMdsalManager(mdsalManager); + + //Instantiate ExternalNetworkListener and set the MdsalManager in it. + extNwListener = new ExternalNetworkListener(dataBroker); + extNwListener.setMdsalManager(mdsalManager); + + //Instantiate NaptManager and set the IdManagerService in it. + naptManager = new NaptManager(dataBroker); + naptManager.setIdManager(idManager); + + //Instantiate NaptEventHandler and start it as a Thread. + naptEventHandler = new NaptEventHandler(dataBroker); + naptEventHandler.setMdsalManager(mdsalManager); + naptEventHandler.setNaptManager(naptManager); + naptEventQueue = new ArrayBlockingQueue<>(NatConstants.EVENT_QUEUE_LENGTH); + eventDispatcher = new EventDispatcher(naptEventQueue, naptEventHandler); + new Thread(eventDispatcher).start(); + + //Instantiate NaptPacketInHandler and register it in the notification service. + naptPacketInHandler = new NaptPacketInHandler(eventDispatcher); + notificationService.registerNotificationListener(naptPacketInHandler); + + //Floating ip Handler + VpnRpcService vpnService = rpcProviderRegistry.getRpcService(VpnRpcService.class); + FibRpcService fibService = rpcProviderRegistry.getRpcService(FibRpcService.class); + VpnFloatingIpHandler handler = new VpnFloatingIpHandler(vpnService, bgpManager, fibService); + handler.setBroker(dataBroker); + handler.setMdsalManager(mdsalManager); + handler.setListener(floatingIpListener); + floatingIpListener.setFloatingIpHandler(handler); + + //Instantiate NaptSwitchSelector and set the dataBroker in it. + naptSwitchSelector = new NAPTSwitchSelector( dataBroker ); + + //Instantiate ExternalRouterListener and set the dataBroker in it. + externalRouterListener = new ExternalRoutersListener( dataBroker ); + externalRouterListener.registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker ); + externalRouterListener.setMdsalManager(mdsalManager); + externalRouterListener.setItmManager(itmManager); + externalRouterListener.setInterfaceManager(interfaceService); + externalRouterListener.setIdManager(idManager); + externalRouterListener.setNaptManager(naptManager); + externalRouterListener.setBgpManager(bgpManager); + externalRouterListener.setFibService(fibService); + externalRouterListener.setVpnService(vpnService); + externalRouterListener.setNaptSwitchSelector(naptSwitchSelector); + + //Instantiate ExternalNetworksChangeListener and set the dataBroker in it. + externalNetworksChangeListener = new ExternalNetworksChangeListener( dataBroker ); + externalNetworksChangeListener.registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker); + externalNetworksChangeListener.setMdsalManager(mdsalManager); + externalNetworksChangeListener.setInterfaceManager(interfaceService); + externalNetworksChangeListener.setFloatingIpListener(floatingIpListener); + externalNetworksChangeListener.setBgpManager(bgpManager); + externalNetworksChangeListener.setFibService(fibService); + externalNetworksChangeListener.setVpnService(vpnService); + externalNetworksChangeListener.setExternalRoutersListener(externalRouterListener); + externalNetworksChangeListener.setNaptManager(naptManager); + externalNetworksChangeListener.setExternalRoutersListener(externalRouterListener); + + //Instantiate NaptFlowRemovedHandler and register it in the notification service. + naptFlowRemovedEventHandler = new NaptFlowRemovedEventHandler(eventDispatcher, dataBroker, naptPacketInHandler, mdsalManager, naptManager); + notificationService.registerNotificationListener(naptFlowRemovedEventHandler); + + //Instantiate interfaceStateEventListener and set the MdsalManager in it. + interfaceStateEventListener = new InterfaceStateEventListener(dataBroker); + interfaceStateEventListener.setMdsalManager(mdsalManager); + interfaceStateEventListener.setFloatingIpListener(floatingIpListener); + interfaceStateEventListener.setNeutronVpnService(neutronvpnService); + interfaceStateEventListener.setNaptManager(naptManager); + + SNATDefaultRouteProgrammer defaultRouteProgrammer = new SNATDefaultRouteProgrammer(mdsalManager); + DpnInVpnListener dpnInVpnListener = new DpnInVpnListener(dataBroker); + dpnInVpnListener.setDefaultProgrammer(defaultRouteProgrammer); + notificationService.registerNotificationListener(dpnInVpnListener); + + externalRouterListener.setDefaultProgrammer(defaultRouteProgrammer); + + NaptSwitchHA naptSwitchHA = new NaptSwitchHA(dataBroker,naptSwitchSelector); + naptSwitchHA.setIdManager(idManager); + naptSwitchHA.setInterfaceManager(interfaceService); + naptSwitchHA.setMdsalManager(mdsalManager); + naptSwitchHA.setItmManager(itmManager); + naptSwitchHA.setBgpManager(bgpManager); + naptSwitchHA.setFibService(fibService); + naptSwitchHA.setVpnService(vpnService); + naptSwitchHA.setExternalRoutersListener(externalRouterListener); + + natNodeEventListener = new NatNodeEventListener(dataBroker,naptSwitchHA); + + dpnInVpnListener.setNaptSwitchHA(naptSwitchHA); + dpnInVpnListener.setMdsalManager(mdsalManager); + dpnInVpnListener.setIdManager(idManager); + + routerPortsListener = new RouterPortsListener(dataBroker); + } catch (Exception e) { + LOG.error("Error initializing NAT Manager service", e); + } + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatUtil.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatUtil.java new file mode 100644 index 00000000..166bb722 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatUtil.java @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces; +import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface; +import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.*; +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; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolTypeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIds; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMapKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPortKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.FloatingIpInfo; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.VpnMaps; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.vpnmaps.VpnMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.vpnmaps.VpnMapKey; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; + +import com.google.common.base.Optional; +import org.opendaylight.bgpmanager.api.IBgpManager; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.NetworksKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPortsKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchKey; +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.VpnInstanceOpDataEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.DpnEndpoints; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.dpn.endpoints.DPNTEPsInfo; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.dpn.endpoints.DPNTEPsInfoKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.dpn.endpoints.dpn.teps.info.TunnelEndPoints; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpPortMap; +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.IpPortMappingKey; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + + + +public class NatUtil { + + private static String OF_URI_SEPARATOR = ":"; + private static final Logger LOG = LoggerFactory.getLogger(NatUtil.class); + + /* + getCookieSnatFlow() computes and returns a unique cookie value for the NAT flows using the router ID as the reference value. + */ + public static BigInteger getCookieSnatFlow(long routerId) { + return NatConstants.COOKIE_NAPT_BASE.add(new BigInteger("0110000", 16)).add( + BigInteger.valueOf(routerId)); + } + + /* + getCookieNaptFlow() computes and returns a unique cookie value for the NAPT flows using the router ID as the reference value. + */ + public static BigInteger getCookieNaptFlow(long routerId) { + return NatConstants.COOKIE_NAPT_BASE.add(new BigInteger("0111000", 16)).add( + BigInteger.valueOf(routerId)); + } + + /* + getVpnId() returns the VPN ID from the VPN name + */ + public static long getVpnId(DataBroker broker, String vpnName) { + + InstanceIdentifier id + = getVpnInstanceToVpnIdIdentifier(vpnName); + Optional vpnInstance + = read(broker, LogicalDatastoreType.CONFIGURATION, id); + + long vpnId = NatConstants.INVALID_ID; + if(vpnInstance.isPresent()) { + vpnId = vpnInstance.get().getVpnId(); + } + return vpnId; + } + + static InstanceIdentifier getRouterPortsId(String routerId) { + return InstanceIdentifier.builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerId)).build(); + } + + static InstanceIdentifier getPortsIdentifier(String routerId, String portName) { + return InstanceIdentifier.builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerId)) + .child(Ports.class, new PortsKey(portName)).build(); + } + + + static InstanceIdentifier getIpMappingIdentifier(String routerId, String portName, String internalIp) { + return InstanceIdentifier.builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerId)) + .child(Ports.class, new PortsKey(portName)) + .child(IpMapping.class, new IpMappingKey(internalIp)).build(); + } + + /* + getVpnInstanceToVpnIdIdentifier() returns the VPN instance from the below model using the VPN name as the key. + list vpn-instance { + key "vpn-instance-name" + leaf vpn-instance-name { + type string; + } + leaf vpn-id { + type uint32; + } + leaf vrf-id { + type string; + } + } + */ + + static InstanceIdentifier + getVpnInstanceToVpnIdIdentifier(String vpnName) { + return InstanceIdentifier.builder(VpnInstanceToVpnId.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance.class, + new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceKey(vpnName)).build(); + } + + /* + getFlowRef() returns a string identfier for the SNAT flows using the router ID as the reference. + */ + public static String getFlowRef(BigInteger dpnId, short tableId, String routerID) { + return new StringBuffer().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR). + append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString(); + } + + public static String getNaptFlowRef(BigInteger dpnId, short tableId, String routerID, String ip, int port) { + return new StringBuffer().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR). + append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).append(NatConstants.FLOWID_SEPARATOR).append(ip). + append(NatConstants.FLOWID_SEPARATOR).append(port).toString(); + } + + /* + getNetworkIdFromRouterId() returns the network-id from the below model using the router-id as the key + container ext-routers { + list routers { + key router-name; + leaf router-name { type string; } + leaf network-id { type yang:uuid; } + leaf enable-snat { type boolean; } + leaf-list external-ips { + type string; //format - ipaddress\prefixlength + } + leaf-list subnet-ids { type yang:uuid; } + } + } + + */ + static Uuid getNetworkIdFromRouterId(DataBroker broker, long routerId) { + String routerName = getRouterName(broker, routerId); + InstanceIdentifier id = buildRouterIdentifier(routerName); + Optional routerData = read(broker, LogicalDatastoreType.CONFIGURATION, id); + if (routerData.isPresent()) { + return routerData.get().getNetworkId(); + } + return null; + } + + static InstanceIdentifier buildRouterIdentifier(String routerId) { + InstanceIdentifier routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child + (Routers.class, new RoutersKey(routerId)).build(); + return routerInstanceIndentifier; + } + /* + * getEnableSnatFromRouterId() returns IsSnatEnabled true is routerID is present in external n/w otherwise returns false + */ + static boolean isSnatEnabledForRouterId(DataBroker broker, String routerId){ + InstanceIdentifier id = buildRouterIdentifier(routerId); + Optional routerData = read(broker, LogicalDatastoreType.CONFIGURATION, id); + if (routerData.isPresent()) { + return routerData.get().isEnableSnat(); + } + return false; + } + /* + getVpnIdfromNetworkId() returns the vpnid from the below model using the network ID as the key. + container external-networks { + list networks { + key id; + leaf id { + type yang:uuid; + } + leaf vpnid { type yang:uuid; } + leaf-list router-ids { type yang:uuid; } + leaf-list subnet-ids{ type yang:uuid; } + } + } + */ + public static Uuid getVpnIdfromNetworkId(DataBroker broker, Uuid networkId) { + InstanceIdentifier id = buildNetworkIdentifier(networkId); + Optional networkData = read(broker, LogicalDatastoreType.CONFIGURATION, id); + if (networkData.isPresent()) { + return networkData.get().getVpnid(); + } + return null; + } + + private static InstanceIdentifier buildNetworkIdentifier(Uuid networkId) { + InstanceIdentifier network = InstanceIdentifier.builder(ExternalNetworks.class).child + (Networks.class, new NetworksKey(networkId)).build(); + return network; + } + + + + /* + getNaptSwitchesDpnIdsfromRouterId() returns the primary-switch-id and the secondary-switch-id in a array using the router-id; as the key. + container napt-switches { + list router-to-napt-switch { + key router-id; + leaf router-id { type uint32; } + leaf primary-switch-id { type uint64; } + leaf secondary-switch-id { type uint64; } + } + } + */ + public static BigInteger getPrimaryNaptfromRouterId(DataBroker broker, Long routerId) { + // convert routerId to Name + String routerName = getRouterName(broker, routerId); + InstanceIdentifier id = buildNaptSwitchIdentifier(routerName); + Optional routerToNaptSwitchData = read(broker, LogicalDatastoreType.OPERATIONAL, id); + if (routerToNaptSwitchData.isPresent()) { + RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get(); + return routerToNaptSwitchInstance.getPrimarySwitchId(); + } + return null; + } + + private static InstanceIdentifier buildNaptSwitchIdentifier(String routerId) { + InstanceIdentifier rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class).child + (RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerId)).build(); + return rtrNaptSw; + } + + public static String getRouterName(DataBroker broker, Long routerId) { + InstanceIdentifier id = buildRouterIdentifier(routerId); + Optional routerIdsData = read(broker, LogicalDatastoreType.CONFIGURATION, id); + if (routerIdsData.isPresent()) { + RouterIds routerIdsInstance = routerIdsData.get(); + return routerIdsInstance.getRouterName(); + } + return null; + } + + private static InstanceIdentifier buildRouterIdentifier(Long routerId) { + InstanceIdentifier routerIds = InstanceIdentifier.builder(RouterIdName.class).child + (RouterIds.class, new RouterIdsKey(routerId)).build(); + return routerIds; + } + + public static Optional read(DataBroker broker, LogicalDatastoreType datastoreType, + InstanceIdentifier path) { + + ReadOnlyTransaction tx = broker.newReadOnlyTransaction(); + + Optional result = Optional.absent(); + try { + result = tx.read(datastoreType, path).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return result; + } + + static InstanceIdentifier getVpnInstanceOpDataIdentifier(String vrfId) { + return InstanceIdentifier.builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vrfId)).build(); + } + + public static long readVpnId(DataBroker broker, String vpnName) { + + InstanceIdentifier id + = getVpnInstanceToVpnIdIdentifier(vpnName); + Optional vpnInstance + = read(broker, LogicalDatastoreType.CONFIGURATION, id); + + long vpnId = NatConstants.INVALID_ID; + if(vpnInstance.isPresent()) { + vpnId = vpnInstance.get().getVpnId(); + } + return vpnId; + } + + public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, BigInteger cookie) { + FlowEntity flowEntity = new FlowEntity(dpnId); + flowEntity.setTableId(tableId); + flowEntity.setCookie(cookie); + return flowEntity; + } + + public static long getIpAddress(byte[] rawIpAddress) { + return (((rawIpAddress[0] & 0xFF) << (3 * 8)) + ((rawIpAddress[1] & 0xFF) << (2 * 8)) + + ((rawIpAddress[2] & 0xFF) << (1 * 8)) + (rawIpAddress[3] & 0xFF)) & 0xffffffffL; + } + + public static String getFlowRef(BigInteger dpnId, short tableId, InetAddress destPrefix) { + return new StringBuilder(64).append(NatConstants.FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR) + .append(tableId).append(NwConstants.FLOWID_SEPARATOR) + .append(destPrefix.getHostAddress()).toString(); + } + + public static String getEndpointIpAddressForDPN(DataBroker broker, BigInteger dpnId) { + String nextHopIp = null; + InstanceIdentifier tunnelInfoId = + InstanceIdentifier.builder(DpnEndpoints.class).child(DPNTEPsInfo.class, new DPNTEPsInfoKey(dpnId)).build(); + Optional tunnelInfo = read(broker, LogicalDatastoreType.CONFIGURATION, tunnelInfoId); + if (tunnelInfo.isPresent()) { + List nexthopIpList = tunnelInfo.get().getTunnelEndPoints(); + if (nexthopIpList != null && !nexthopIpList.isEmpty()) { + nextHopIp = nexthopIpList.get(0).getIpAddress().getIpv4Address().getValue(); + } + } + return nextHopIp; + } + + /* + getVpnRd returns the rd (route distinguisher) which is the VRF ID from the below model using the vpnName + list vpn-instance { + key "vpn-instance-name" + leaf vpn-instance-name { + type string; + } + leaf vpn-id { + type uint32; + } + leaf vrf-id { + type string; + } + } + */ + public static String getVpnRd(DataBroker broker, String vpnName) { + + InstanceIdentifier id + = getVpnInstanceToVpnIdIdentifier(vpnName); + Optional vpnInstance + = read(broker, LogicalDatastoreType.CONFIGURATION, id); + + String rd = null; + if(vpnInstance.isPresent()) { + rd = vpnInstance.get().getVrfId(); + } + return rd; + } + + /* getExternalIPPortMap() returns the internal IP and the port for the querried router ID, external IP and the port. + container intext-ip-port-map { + config true; + list ip-port-mapping { + key router-id; + leaf router-id { type uint32; } + list intext-ip-protocol-type { + key protocol; + leaf protocol { type protocol-types; } + list ip-port-map { + key ip-port-internal; + description "internal to external ip-port mapping"; + leaf ip-port-internal { type string; } + container ip-port-external { + uses ip-port-entity; + } + } + } + } + } + */ + public static IpPortExternal getExternalIpPortMap(DataBroker broker, Long routerId, String internalIpAddress, String internalPort, NAPTEntryEvent.Protocol protocol) { + ProtocolTypes protocolType = NatUtil.getProtocolType(protocol); + InstanceIdentifier ipPortMapId = buildIpToPortMapIdentifier(routerId, internalIpAddress, internalPort, protocolType); + Optional ipPortMapData = read(broker, LogicalDatastoreType.CONFIGURATION, ipPortMapId); + if (ipPortMapData.isPresent()) { + IpPortMap ipPortMapInstance = ipPortMapData.get(); + return ipPortMapInstance.getIpPortExternal(); + } + return null; + } + + private static InstanceIdentifier buildIpToPortMapIdentifier(Long routerId, String internalIpAddress, String internalPort , ProtocolTypes protocolType) { + InstanceIdentifier ipPortMapId = InstanceIdentifier.builder(IntextIpPortMap.class).child + (IpPortMapping.class, new IpPortMappingKey(routerId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType)) + .child(IpPortMap.class, new IpPortMapKey(internalIpAddress + ":" + internalPort)).build(); + return ipPortMapId; + } + + public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, String flowId, int priority, String flowName, + BigInteger cookie, List listMatchInfo) { + + FlowEntity flowEntity = new FlowEntity(dpnId); + flowEntity.setTableId(tableId); + flowEntity.setFlowId(flowId); + flowEntity.setPriority(priority); + flowEntity.setFlowName(flowName); + flowEntity.setCookie(cookie); + flowEntity.setMatchInfoList(listMatchInfo); + return flowEntity; + } + + static boolean isVpnInterfaceConfigured(DataBroker broker, String interfaceName) + { + InstanceIdentifier interfaceId = getVpnInterfaceIdentifier(interfaceName); + Optional configuredVpnInterface = read(broker, LogicalDatastoreType.CONFIGURATION, interfaceId); + + if (configuredVpnInterface.isPresent()) { + return true; + } + return false; + } + + static InstanceIdentifier getVpnInterfaceIdentifier(String vpnInterfaceName) { + return InstanceIdentifier.builder(VpnInterfaces.class) + .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).build(); + } + + static VpnInterface getConfiguredVpnInterface(DataBroker broker, String interfaceName) { + InstanceIdentifier interfaceId = getVpnInterfaceIdentifier(interfaceName); + Optional configuredVpnInterface = read(broker, LogicalDatastoreType.CONFIGURATION, interfaceId); + + if (configuredVpnInterface.isPresent()) { + return configuredVpnInterface.get(); + } + return null; + } + + public static String getDpnFromNodeConnectorId(NodeConnectorId portId) { + /* + * NodeConnectorId is of form 'openflow:dpnid:portnum' + */ + String[] split = portId.getValue().split(OF_URI_SEPARATOR); + return split[1]; + } + + public static BigInteger getDpIdFromInterface(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState) { + String lowerLayerIf = ifState.getLowerLayerIf().get(0); + NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf); + return new BigInteger(getDpnFromNodeConnectorId(nodeConnectorId)); + } + + + /* + container vpnMaps { + list vpnMap { + key vpn-id; + leaf vpn-id { + type yang:uuid; + description "vpn-id"; + } + leaf name { + type string; + description "vpn name"; + } + leaf tenant-id { + type yang:uuid; + description "The UUID of the tenant that will own the subnet."; + } + + leaf router-id { + type yang:uuid; + description "UUID of router "; + } + leaf-list network_ids { + type yang:uuid; + description "UUID representing the network "; + } + } + } + Method returns router Id associated to a VPN + */ + + public static String getRouterIdfromVpnId(DataBroker broker,String vpnName){ + InstanceIdentifier vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class) + .child(VpnMap.class, new VpnMapKey(new Uuid(vpnName))).build(); + Optional optionalVpnMap = read(broker, LogicalDatastoreType.CONFIGURATION, + vpnMapIdentifier); + if (optionalVpnMap.isPresent()) { + return optionalVpnMap.get().getRouterId().getValue(); + } + return null; + } + + + public static List getVpnToDpnList(DataBroker dataBroker, String vrfId ) + { + List vpnDpnList = null; + + InstanceIdentifier id = InstanceIdentifier + .builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vrfId)) + .build(); + + Optional vpnInstanceOpData = read(dataBroker, LogicalDatastoreType.OPERATIONAL, id); + + if(vpnInstanceOpData.isPresent()) + { + VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnInstanceOpData.get(); + vpnDpnList = vpnInstanceOpDataEntry.getVpnToDpnList(); + } + + return vpnDpnList; + } + + public static String getAssociatedVPN(DataBroker dataBroker, Uuid networkId, Logger log) { + Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId); + if(vpnUuid == null ){ + log.error("No VPN instance associated with ext network {}", networkId); + return null; + } + return vpnUuid.getValue(); + } + + public static void addPrefixToBGP(IBgpManager bgpManager, String rd, String prefix, String nextHopIp, long label, Logger log) { + try { + bgpManager.addPrefix(rd, prefix, nextHopIp, (int)label); + } catch(Exception e) { + log.error("Add prefix failed", e); + } + } + + static InstanceIdentifier buildPortToIpMapIdentifier(String routerId, String portName) { + InstanceIdentifier ipPortMapId = InstanceIdentifier.builder(FloatingIpInfo.class).child + (RouterPorts.class, new RouterPortsKey(routerId)).child(Ports.class, new PortsKey(portName)).build(); + return ipPortMapId; + } + + static InstanceIdentifier buildRouterPortsIdentifier(String routerId) { + InstanceIdentifier routerInstanceIndentifier = InstanceIdentifier.builder(FloatingIpInfo.class).child + (RouterPorts.class, new RouterPortsKey(routerId)).build(); + return routerInstanceIndentifier; + } + + /* container snatint-ip-port-map { + list intip-port-map { + key router-id; + leaf router-id { type uint32; } + list ip-port { + key internal-ip; + leaf internal-ip { type string; } + list int-ip-proto-type { + key protocol; + leaf protocol { type protocol-types; } + leaf-list ports { type uint16; } + } + } + } + } + Method returns InternalIp port List + */ + + public static List getInternalIpPortListInfo(DataBroker dataBroker,Long routerId, String internalIpAddress, ProtocolTypes protocolType){ + Optional optionalIpProtoType = read(dataBroker, LogicalDatastoreType.CONFIGURATION, buildSnatIntIpPortIdentifier(routerId, internalIpAddress, protocolType)); + if (optionalIpProtoType.isPresent()) { + return optionalIpProtoType.get().getPorts(); + } + return null; + } + + public static InstanceIdentifier buildSnatIntIpPortIdentifier(Long routerId, String internalIpAddress, ProtocolTypes protocolType) { + InstanceIdentifier intIpProtocolTypeId = InstanceIdentifier.builder(SnatintIpPortMap.class).child + (IntipPortMap.class, new IntipPortMapKey(routerId)).child(IpPort.class, new IpPortKey(internalIpAddress)).child + (IntIpProtoType.class, new IntIpProtoTypeKey(protocolType)).build(); + return intIpProtocolTypeId; + } + + public static ProtocolTypes getProtocolType(NAPTEntryEvent.Protocol protocol) { + ProtocolTypes protocolType = ProtocolTypes.TCP.toString().equals(protocol.toString()) ? ProtocolTypes.TCP : ProtocolTypes.UDP; + return protocolType; + } + + public static NaptSwitches getNaptSwitch(DataBroker broker) { + Optional switchesOptional = read(broker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier()); + if(switchesOptional.isPresent()) { + return switchesOptional.get(); + } + return null; + } + + public static InstanceIdentifier getNaptSwitchesIdentifier() { + return InstanceIdentifier.create(NaptSwitches.class); + } + + public static InstanceIdentifier buildNaptSwitchRouterIdentifier(String routerId) { + return InstanceIdentifier.create(NaptSwitches.class).child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerId)); + } + + public static String toStringIpAddress(byte[] ipAddress, Logger log) + { + String ip = ""; + if (ipAddress == null) { + return ip; + } + + try { + ip = InetAddress.getByAddress(ipAddress).getHostAddress(); + } catch(UnknownHostException e) { + log.error("NAT Service : Caught exception during toStringIpAddress()"); + } + + return ip; + } + + public static String getGroupIdKey(String routerName){ + String groupIdKey = new String("snatmiss." + routerName); + return groupIdKey; + } + + public static long createGroupId(String groupIdKey,IdManagerService idManager) { + AllocateIdInput getIdInput = new AllocateIdInputBuilder() + .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey) + .build(); + try { + Future> result = idManager.allocateId(getIdInput); + RpcResult rpcResult = result.get(); + return rpcResult.getResult().getIdValue(); + } catch (NullPointerException | InterruptedException | ExecutionException e) { + LOG.trace("", e); + } + return 0; + } + + public static void removePrefixFromBGP(IBgpManager bgpManager, String rd, String prefix, Logger log) { + try { + bgpManager.deletePrefix(rd, prefix); + } catch(Exception e) { + log.error("Delete prefix failed", e); + } + } + + public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, BigInteger cookie, String flowId) { + FlowEntity flowEntity = new FlowEntity(dpnId); + flowEntity.setTableId(tableId); + flowEntity.setCookie(cookie); + flowEntity.setFlowId(flowId); + return flowEntity; + } + + public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, String flowId) { + FlowEntity flowEntity = new FlowEntity(dpnId); + flowEntity.setTableId(tableId); + flowEntity.setFlowId(flowId); + return flowEntity; + } + + public static IpPortMapping getIportMapping(DataBroker broker, long routerId) { + Optional getIportMappingData = read(broker, LogicalDatastoreType.CONFIGURATION, getIportMappingIdentifier(routerId)); + if(getIportMappingData.isPresent()) { + return getIportMappingData.get(); + } + return null; + } + + public static InstanceIdentifier getIportMappingIdentifier(long routerId) { + return InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(routerId)).build(); + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/RouterPortsListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/RouterPortsListener.java new file mode 100644 index 00000000..b5b8d52e --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/RouterPortsListener.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.FloatingIpInfo; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPortsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPortsKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +public class RouterPortsListener extends AbstractDataChangeListener implements AutoCloseable{ + private static final Logger LOG = LoggerFactory.getLogger(RouterPortsListener.class); + private ListenerRegistration listenerRegistration; + private final DataBroker broker; + + + public RouterPortsListener (final DataBroker db) { + super(RouterPorts.class); + broker = db; + registerListener(db); + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + LOG.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + LOG.info("Router ports Listener Closed"); + } + + private void registerListener(final DataBroker db) { + try { + listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, + getWildCardPath(), RouterPortsListener.this, AsyncDataBroker.DataChangeScope.SUBTREE); + } catch (final Exception e) { + LOG.error("RouterPorts DataChange listener registration fail!", e); + throw new IllegalStateException("RouterPorts Listener registration Listener failed.", e); + } + } + + private InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(FloatingIpInfo.class).child(RouterPorts.class); + } + + + @Override + protected void add(final InstanceIdentifier identifier, final RouterPorts routerPorts) { + LOG.trace("Add router ports method - key: " + identifier + ", value=" + routerPorts ); + Optional optRouterPorts = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, identifier); + if(optRouterPorts.isPresent()) { + RouterPorts ports = optRouterPorts.get(); + String routerName = ports.getRouterId(); + MDSALUtil.syncUpdate(broker, LogicalDatastoreType.OPERATIONAL, identifier, + new RouterPortsBuilder().setKey(new RouterPortsKey(routerName)).setRouterId(routerName) + .setExternalNetworkId(routerPorts.getExternalNetworkId()).build()); + } else { + String routerName = routerPorts.getRouterId(); + MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, identifier, + new RouterPortsBuilder().setKey(new RouterPortsKey(routerName)).setRouterId(routerName) + .setExternalNetworkId(routerPorts.getExternalNetworkId()).build()); + } + } + + @Override + protected void remove(InstanceIdentifier identifier, RouterPorts routerPorts) { + LOG.trace("Remove router ports method - key: " + identifier + ", value=" + routerPorts ); + //MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, identifier); + + } + + @Override + protected void update(InstanceIdentifier identifier, RouterPorts original, RouterPorts update) { + LOG.trace("Update router ports method - key: " + identifier + ", original=" + original + ", update=" + update ); + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SNATDefaultRouteProgrammer.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SNATDefaultRouteProgrammer.java new file mode 100644 index 00000000..6c79bb16 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SNATDefaultRouteProgrammer.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SNATDefaultRouteProgrammer { + + private static final Logger LOG = LoggerFactory.getLogger(SNATDefaultRouteProgrammer.class); + private IMdsalApiManager mdsalManager; + + public SNATDefaultRouteProgrammer(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + private FlowEntity buildDefNATFlowEntity(BigInteger dpId, long vpnId) { + + InetAddress defaultIP = null; + + try { + defaultIP = InetAddress.getByName("0.0.0.0"); + + } catch (UnknownHostException e) { + LOG.error("UnknowHostException in buildDefNATFlowEntity. Failed to build FIB Table Flow for Default Route to NAT table "); + return null; + } + + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + //add match for default route "0.0.0.0/0" +// matches.add(new MatchInfo(MatchFieldType.ipv4_dst, new long[] { +// NatUtil.getIpAddress(defaultIP.getAddress()), 0 })); + + //add match for vrfid + matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { + BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID })); + + List instructions = new ArrayList(); + instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PSNAT_TABLE })); + + String flowRef = getFlowRefFib(dpId, NatConstants.L3_FIB_TABLE, vpnId); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.L3_FIB_TABLE, flowRef, + NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0, + NatConstants.COOKIE_DNAT_TABLE, matches, instructions); + + return flowEntity; + + + } + + private String getFlowRefFib(BigInteger dpnId, short tableId, long routerID) { + return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR). + append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString(); + } + + void installDefNATRouteInDPN(BigInteger dpnId, long vpnId) { + FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId); + if(flowEntity == null) { + LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow"); + return; + } + mdsalManager.installFlow(flowEntity); + } + + void removeDefNATRouteInDPN(BigInteger dpnId, long vpnId) { + FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId); + if(flowEntity == null) { + LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow"); + return; + } + mdsalManager.removeFlow(flowEntity); + } + +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SessionAddress.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SessionAddress.java new file mode 100644 index 00000000..eee5d4df --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SessionAddress.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal; + +public class SessionAddress { + + private String ipAddress; + private int portNumber; + + public SessionAddress(String ipAddress, int portNumber) { + this.ipAddress = ipAddress; + this.portNumber = portNumber; + } + public String getIpAddress() { + return ipAddress; + } + public int getPortNumber() { + return portNumber; + } + +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/VpnFloatingIpHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/VpnFloatingIpHandler.java new file mode 100644 index 00000000..8974ebb7 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/VpnFloatingIpHandler.java @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.vpnservice.natservice.internal; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.concurrent.Future; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.bgpmanager.api.IBgpManager; +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.JdkFutureAdapters; +import com.google.common.util.concurrent.ListenableFuture; + +public class VpnFloatingIpHandler implements FloatingIPHandler { + private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class); + private VpnRpcService vpnService; + private FibRpcService fibService; + private IBgpManager bgpManager; + private DataBroker dataBroker; + private IMdsalApiManager mdsalManager; + private FloatingIPListener listener; + + static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16); + static final String FLOWID_PREFIX = "NAT."; + static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16); + + public VpnFloatingIpHandler(VpnRpcService vpnService, IBgpManager bgpManager, FibRpcService fibService) { + this.vpnService = vpnService; + this.fibService = fibService; + this.bgpManager = bgpManager; + } + + void setListener(FloatingIPListener listener) { + this.listener = listener; + } + + void setBroker(DataBroker broker) { + dataBroker = broker; + } + + void setMdsalManager(IMdsalApiManager mdsalManager) { + this.mdsalManager = mdsalManager; + } + + @Override + public void onAddFloatingIp(final BigInteger dpnId, final String routerId, + Uuid networkId, final String interfaceName, final String externalIp, final String internalIp) { + final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG); + if(vpnName == null) { + LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}", + networkId, externalIp, routerId); + return; + } + + GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build(); + Future> labelFuture = vpnService.generateVpnLabel(labelInput); + + ListenableFuture> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction, RpcResult>() { + + @Override + public ListenableFuture> apply(RpcResult result) throws Exception { + if(result.isSuccessful()) { + GenerateVpnLabelOutput output = result.getResult(); + long label = output.getLabel(); + LOG.debug("Generated label {} for prefix {}", label, externalIp); + listener.updateOperationalDS(routerId, interfaceName, (int)label, internalIp, externalIp); + + //Inform BGP + String rd = NatUtil.getVpnRd(dataBroker, vpnName); + String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId); + LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp); + NatUtil.addPrefixToBGP(bgpManager, rd, externalIp + "/32", nextHopIp, label, LOG); + + List instructions = new ArrayList(); + List actionsInfos = new ArrayList(); + actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.PDNAT_TABLE) })); + instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0)); + makeTunnelTableEntry(dpnId, label, instructions); + makeLFibTableEntry(dpnId, label, instructions); + + //Install custom FIB routes + List customInstructions = new ArrayList<>(); + customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PDNAT_TABLE }).buildInstruction(0)); + CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setInstruction(customInstructions) + .setIpAddress(externalIp + "/32").setServiceId(label).setInstruction(customInstructions).build(); + //Future> createFibEntry(CreateFibEntryInput input); + Future> future = fibService.createFibEntry(input); + return JdkFutureAdapters.listenInPoolThread(future); + } else { + String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors()); + LOG.error(errMsg); + return Futures.immediateFailedFuture(new RuntimeException(errMsg)); + } + } + }); + + Futures.addCallback(future, new FutureCallback>() { + + @Override + public void onFailure(Throwable error) { + LOG.error("Error in generate label or fib install process", error); + } + + @Override + public void onSuccess(RpcResult result) { + if(result.isSuccessful()) { + LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp); + } else { + LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors()); + } + } + }); + } + + @Override + public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, Uuid networkId, final String externalIp, + String internalIp, final long label) { + final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG); + if(vpnName == null) { + LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}", + networkId, externalIp, routerId); + return; + } + //Remove Prefix from BGP + String rd = NatUtil.getVpnRd(dataBroker, vpnName); + removePrefixFromBGP(rd, externalIp + "/32"); + + //Remove custom FIB routes + //Future> removeFibEntry(RemoveFibEntryInput input); + RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build(); + Future> future = fibService.removeFibEntry(input); + + ListenableFuture> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction, RpcResult>() { + + @Override + public ListenableFuture> apply(RpcResult result) throws Exception { + //Release label + if(result.isSuccessful()) { + removeTunnelTableEntry(dpnId, label); + removeLFibTableEntry(dpnId, label); + RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build(); + Future> labelFuture = vpnService.removeVpnLabel(labelInput); + return JdkFutureAdapters.listenInPoolThread(labelFuture); + } else { + String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors()); + LOG.error(errMsg); + return Futures.immediateFailedFuture(new RuntimeException(errMsg)); + } + } + }); + + Futures.addCallback(labelFuture, new FutureCallback>() { + + @Override + public void onFailure(Throwable error) { + LOG.error("Error in removing the label or custom fib entries", error); + } + + @Override + public void onSuccess(RpcResult result) { + if(result.isSuccessful()) { + LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName); + } else { + LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors()); + } + } + }); + } + + private void removePrefixFromBGP(String rd, String prefix) { + try { + bgpManager.deletePrefix(rd, prefix); + } catch(Exception e) { + LOG.error("Delete prefix failed", e); + } + } + + void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp, final long label ) { + //Remove Prefix from BGP + String rd = NatUtil.getVpnRd(dataBroker, vpnName); + removePrefixFromBGP(rd, externalIp + "/32"); + + //Remove custom FIB routes + + //Future> removeFibEntry(RemoveFibEntryInput input); + 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>() { + + @Override + public ListenableFuture> apply(RpcResult result) throws Exception { + //Release label + if(result.isSuccessful()) { + removeTunnelTableEntry(dpnId, label); + removeLFibTableEntry(dpnId, label); + RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build(); + Future> labelFuture = vpnService.removeVpnLabel(labelInput); + return JdkFutureAdapters.listenInPoolThread(labelFuture); + } else { + String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors()); + LOG.error(errMsg); + return Futures.immediateFailedFuture(new RuntimeException(errMsg)); + } + } + }); + + Futures.addCallback(labelFuture, new FutureCallback>() { + + @Override + public void onFailure(Throwable error) { + LOG.error("Error in removing the label or custom fib entries", error); + } + + @Override + public void onSuccess(RpcResult result) { + if(result.isSuccessful()) { + LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName); + } else { + LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors()); + } + } + }); + } + + private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) { + return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR) + .append(tableId).append(NwConstants.FLOWID_SEPARATOR) + .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString(); + } + + private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) { + LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId); + List mkMatches = new ArrayList(); + // Matching metadata + mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)})); + Flow flowEntity = 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, null); + mdsalManager.removeFlow(dpnId, flowEntity); + LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId); + } + + private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List customInstructions) { + List mkMatches = new ArrayList(); + + LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId); + + 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); + + mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity); + } + + private void makeLFibTableEntry(BigInteger dpId, long serviceId, List customInstructions) { + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + 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); + instructions.add(writeInstruction); + instructions.addAll(customInstructions); + + // Install the flow entry in L3_LFIB_TABLE + String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, ""); + + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef, + 10, flowRef, 0, 0, + COOKIE_VM_LFIB_TABLE, matches, instructions); + + mdsalManager.installFlow(dpId, flowEntity); + + LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId ); + } + + private void removeLFibTableEntry(BigInteger dpnId, long serviceId) { + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x8847L })); + matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)})); + + String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, ""); + + LOG.debug("removing LFib entry with flow ref {}", flowRef); + + Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef, + 10, flowRef, 0, 0, + COOKIE_VM_LFIB_TABLE, matches, null); + + mdsalManager.removeFlow(dpnId, flowEntity); + + LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId); + } + +} + diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModule.java b/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModule.java new file mode 100644 index 00000000..6d346594 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModule.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111; + +import org.opendaylight.vpnservice.natservice.internal.NatServiceProvider; + +public class NATServiceModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111.AbstractNATServiceModule { + public NATServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public NATServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111.NATServiceModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + NatServiceProvider provider = new NatServiceProvider(getRpcRegistryDependency()); + provider.setNotificationService(getNotificationServiceDependency()); + provider.setMdsalManager(getMdsalutilDependency()); + provider.setBgpManager(getBgpmanagerDependency()); + getBrokerDependency().registerProvider(provider); + return provider; + } +} diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModuleFactory.java b/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModuleFactory.java new file mode 100644 index 00000000..d2c93325 --- /dev/null +++ b/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: natmanager-impl yang module local name: natservice-impl +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed Jan 20 15:47:25 IST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111; +public class NATServiceModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111.AbstractNATServiceModuleFactory { + +} diff --git a/natservice/natservice-impl/src/main/yang/natservice-impl.yang b/natservice/natservice-impl/src/main/yang/natservice-impl.yang new file mode 100644 index 00000000..4c71f985 --- /dev/null +++ b/natservice/natservice-impl/src/main/yang/natservice-impl.yang @@ -0,0 +1,70 @@ +module natservice-impl { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:natservice:impl"; + prefix "natservice-impl"; + + import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;} + import opendaylight-sal-binding-broker-impl { prefix md-sal-binding-impl; revision-date 2013-10-28; } + import odl-mdsalutil { prefix odl-mdsal; revision-date 2015-04-10;} + import bgpmanager-api { prefix bgpmgr-api; revision-date 2015-04-20;} + + description + "Service definition for NAT Service module"; + + revision "2016-01-11" { + description + "Initial revision"; + } + + identity natservice-impl { + base config:module-type; + config:java-name-prefix NATService; + } + + augment "/config:modules/config:module/config:configuration" { + case natservice-impl { + when "/config:modules/config:module/config:type = 'natservice-impl'"; + container broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-broker-osgi-registry; + } + } + } + container rpc-registry { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-rpc-registry; + } + } + } + container bgpmanager { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity bgpmgr-api:bgpmanager-api; + } + } + } + container notification-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding-impl:binding-new-notification-service; + } + } + } + container mdsalutil { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity odl-mdsal:odl-mdsalutil; + } + } + } + } + } +} \ No newline at end of file diff --git a/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/ExternalNetworksChangeListenerTest.java b/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/ExternalNetworksChangeListenerTest.java new file mode 100644 index 00000000..a284db18 --- /dev/null +++ b/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/ExternalNetworksChangeListenerTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vpnservice.natservice.internal.test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +import org.opendaylight.vpnservice.mdsalutil.BucketInfo; +import org.opendaylight.vpnservice.mdsalutil.FlowEntity; +import org.opendaylight.vpnservice.mdsalutil.GroupEntity; +import org.opendaylight.vpnservice.mdsalutil.InstructionInfo; +import org.opendaylight.vpnservice.mdsalutil.InstructionType; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.vpnservice.natservice.internal.ExternalNetworksChangeListener; +import org.opendaylight.vpnservice.natservice.internal.NatUtil; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + + + +@RunWith(PowerMockRunner.class) +@PrepareForTest(MDSALUtil.class) +public class ExternalNetworksChangeListenerTest { + + @Mock DataBroker dataBroker; + @Mock ListenerRegistration dataChangeListenerRegistration; + @Mock IMdsalApiManager mdsalManager; + @Mock FlowEntity flowMock; + @Mock GroupEntity groupMock; + InstanceIdentifier id = null; + org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks networks = null; + private ExternalNetworksChangeListener extNetworks; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(dataBroker.registerDataChangeListener( + any(LogicalDatastoreType.class), + any(InstanceIdentifier.class), + any(DataChangeListener.class), + any(DataChangeScope.class))) + .thenReturn(dataChangeListenerRegistration); + extNetworks = new ExternalNetworksChangeListener(dataBroker); + + PowerMockito.mockStatic(MDSALUtil.class); + } + + + @Test + public void testSnatFlowEntity() { + FlowEntity flowMock = mock(FlowEntity.class); + final short SNAT_TABLE = 40; + final int DEFAULT_SNAT_FLOW_PRIORITY = 0; + final String FLOWID_SEPARATOR = "."; + String SNAT_FLOWID_PREFIX = "SNAT."; + + + BigInteger dpnId = new BigInteger("100"); + String routerName = new String("200"); + long routerId = 200; + long groupId = 300; + List bucketInfo = new ArrayList(); + List listActionInfoPrimary = new ArrayList(); + listActionInfoPrimary.add(new ActionInfo(ActionType.output, + new String[] {"3"})); + BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary); + List listActionInfoSecondary = new ArrayList(); + listActionInfoSecondary.add(new ActionInfo(ActionType.output, + new String[] {"4"})); + BucketInfo bucketSecondary = new BucketInfo(listActionInfoPrimary); + bucketInfo.add(0, bucketPrimary); + bucketInfo.add(1, bucketSecondary); + + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { 0x0800L })); + + List instructions = new ArrayList(); + List actionsInfos = new ArrayList(); + actionsInfos.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)})); + instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos)); + + + String flowRef = new StringBuffer().append(SNAT_FLOWID_PREFIX).append(dpnId).append(FLOWID_SEPARATOR). + append(SNAT_TABLE).append(FLOWID_SEPARATOR).append(routerId).toString(); + + BigInteger cookieSnat = NatUtil.getCookieSnatFlow(routerId); + try { + PowerMockito.when(MDSALUtil.class, "buildFlowEntity", dpnId, SNAT_TABLE, flowRef, + DEFAULT_SNAT_FLOW_PRIORITY, flowRef, 0, 0, + cookieSnat, matches, instructions ).thenReturn(flowMock); + } catch (Exception e) { + // Test failed anyways + assertEquals("true", "false"); + } + /* TODO : Fix this to mock it properly when it reads DS + extNetworks.buildSnatFlowEntity(dpnId, routerName, groupId); + PowerMockito.verifyStatic(); */ + + } + +} diff --git a/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/NaptManagerTest.java b/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/NaptManagerTest.java new file mode 100644 index 00000000..12194b3b --- /dev/null +++ b/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/NaptManagerTest.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2016 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, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + + +package org.opendaylight.vpnservice.natservice.internal.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.OngoingStubbing; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.eq; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.natservice.internal.IPAddress; +import org.opendaylight.vpnservice.natservice.internal.NaptManager; +import org.opendaylight.vpnservice.natservice.internal.SessionAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpPortMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey; +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.IpPortMappingKey; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; + +import com.google.common.util.concurrent.Futures; + +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + + +@RunWith(PowerMockRunner.class) +@PrepareForTest(MDSALUtil.class) +public class NaptManagerTest { + + @Mock IdManagerService idMgr; + @Mock DataBroker dataBroker; + @Mock ListenerRegistration dataChangeListenerRegistration; + InstanceIdentifier ipmapId = null; + org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap ipmap = null; + + private NaptManager naptManager; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + when(dataBroker.registerDataChangeListener( + any(LogicalDatastoreType.class), + any(InstanceIdentifier.class), + any(DataChangeListener.class), + any(DataChangeScope.class))) + .thenReturn(dataChangeListenerRegistration); + naptManager = new NaptManager(dataBroker); + when(idMgr.createIdPool(any(CreateIdPoolInput.class))) + .thenReturn(Futures.immediateFuture(RpcResultBuilder.success().build())); + naptManager.setIdManager(idMgr); + + PowerMockito.mockStatic(MDSALUtil.class); + + } + + + @Test + public void testRegisterMappingIpIP() { + + // TODO : Issue with Mockito.any() usage, so for now run registerMapping testcases as seperate Tests. This needs to be fixed properly. + ipmapId = InstanceIdentifier.builder( + IntextIpMap.class).child(IpMapping.class, new IpMappingKey(5L)).child(IpMap.class, new IpMapKey("10.0.0.1")).build(); + ipmap = new IpMapBuilder().setKey(new IpMapKey("10.0.0.1")).setInternalIp("10.0.0.1").setExternalIp("192.17.13.1").build(); + try { + PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap); + } catch (Exception e) { + // Test failed anyways + assertEquals("true", "false"); + } + IPAddress internal = new IPAddress("10.0.0.1",0); + IPAddress external = new IPAddress("192.17.13.1", 0); + naptManager.registerMapping(5, internal, external); + PowerMockito.verifyStatic(); + + } + + @Test + public void testRegisterMappingIpSubnet() { + + ipmapId = InstanceIdentifier.builder( + IntextIpMap.class).child(IpMapping.class, new IpMappingKey(5L)).child(IpMap.class, new IpMapKey("10.0.0.1")).build(); + ipmap = new IpMapBuilder().setKey(new IpMapKey("10.0.0.1")).setInternalIp("10.0.0.1").setExternalIp("192.17.13.1/24").build(); + try { + PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap); + } catch (Exception e) { + // Test failed anyways + assertEquals("true", "false"); + } + IPAddress internal = new IPAddress("10.0.0.1",0); + IPAddress external = new IPAddress("192.17.13.1", 24); + naptManager.registerMapping(5, internal, external); + PowerMockito.verifyStatic(); + } + + @Test + public void testRegisterMappingSubnetIp() { + + ipmapId = InstanceIdentifier.builder( + IntextIpMap.class).child(IpMapping.class, new IpMappingKey(6L)).child(IpMap.class, new IpMapKey("10.0.2.1/16")).build(); + ipmap = new IpMapBuilder().setKey(new IpMapKey("10.0.0.1")).setInternalIp("10.0.0.1").setExternalIp("192.19.15.3").build(); + try { + PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap); + } catch (Exception e) { + // Test failed anyways + assertEquals("true", "false"); + } + IPAddress internal = new IPAddress("10.0.2.1",16); + IPAddress external = new IPAddress("192.19.15.3", 0); + naptManager.registerMapping(6, internal, external); + PowerMockito.verifyStatic(); + } + + @Test + public void testRegisterMappingSubnetSubnet() { + + ipmapId = InstanceIdentifier.builder( + IntextIpMap.class).child(IpMapping.class, new IpMappingKey(6L)).child(IpMap.class, new IpMapKey("10.2.0.1/24")).build(); + ipmap = new IpMapBuilder().setKey(new IpMapKey("10.2.0.1/24")).setInternalIp("10.2.0.1/24").setExternalIp("192.21.16.1/16").build(); + try { + PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap); + } catch (Exception e) { + // Test failed anyways + assertEquals("true", "false"); + } + IPAddress internal = new IPAddress("10.2.0.1",24); + IPAddress external = new IPAddress("192.21.16.1", 16); + naptManager.registerMapping(6, internal, external); + PowerMockito.verifyStatic(); + } + + + @Test + public void testgetExternalAddressMapping() { + // TODO : This needs to be modified to make it work + // Testcase to test when no entry exists in ip-pot-map + /*SessionAddress internalIpPort = new SessionAddress("10.0.0.1", 2); + InstanceIdentifierBuilder idBuilder = + InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(5L)); + InstanceIdentifier id = idBuilder.build(); + try { + PowerMockito.when(MDSALUtil.class, "read", dataBroker, LogicalDatastoreType.CONFIGURATION, id).thenReturn(null); + } catch (Exception e) { + // Test failed anyways + assertEquals("true", "false"); + } + naptManager.getExternalAddressMapping(5, internalIpPort); + PowerMockito.verifyStatic(); */ + } + + @Test + public void testReleaseAddressMapping() { + // TODO : Below needs to be modified to make it work + /* InstanceIdentifierBuilder idBuilder = + InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(5L)); + InstanceIdentifier id = idBuilder.build(); + try { + PowerMockito.doNothing().when(MDSALUtil.class, "read", dataBroker, LogicalDatastoreType.OPERATIONAL, id); + } catch (Exception e) { + // Test failed anyways + assertEquals("true", "false"); + } + IPAddress internal = new IPAddress("10.0.0.1",0); + IPAddress external = new IPAddress("192.17.13.1", 0); + naptManager.registerMapping(5, internal, external); + SessionAddress internalSession = new SessionAddress("10.0.0.1", 0); + naptManager.releaseAddressMapping(5L, internalSession);*/ + } + + +} diff --git a/natservice/pom.xml b/natservice/pom.xml new file mode 100644 index 00000000..adba57f1 --- /dev/null +++ b/natservice/pom.xml @@ -0,0 +1,49 @@ + + + + + + org.opendaylight.odlparent + odlparent + 1.7.0-SNAPSHOT + + + + org.opendaylight.vpnservice + natservice-aggregator + 0.3.0-SNAPSHOT + natservice + pom + 4.0.0 + + 3.1.1 + + + natservice-api + natservice-impl + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + diff --git a/pom.xml b/pom.xml index e29557e9..baa7a319 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL neutronvpn dhcpservice itm + natservice distribution/karaf features vpnservice-artifacts diff --git a/vpnmanager/vpnmanager-api/src/main/yang/odl-l3vpn.yang b/vpnmanager/vpnmanager-api/src/main/yang/odl-l3vpn.yang index 6857e98e..384bfb2e 100644 --- a/vpnmanager/vpnmanager-api/src/main/yang/odl-l3vpn.yang +++ b/vpnmanager/vpnmanager-api/src/main/yang/odl-l3vpn.yang @@ -122,6 +122,12 @@ module odl-l3vpn { type string; } } + list ip-addresses { + key ip-address; + leaf ip-address { + type string; + } + } } } } @@ -234,4 +240,22 @@ module odl-l3vpn { } } + grouping dpn-in-vpn-event { + leaf dpn-id { type uint64; } + leaf vpn-name { type string; } + leaf rd { type string; } + } + + notification add-dpn-event { + container add-event-data { + uses dpn-in-vpn-event; + } + } + + notification remove-dpn-event { + container remove-event-data { + uses dpn-in-vpn-event; + } + } + } \ No newline at end of file diff --git a/vpnmanager/vpnmanager-api/src/main/yang/vpn-rpc.yang b/vpnmanager/vpnmanager-api/src/main/yang/vpn-rpc.yang new file mode 100644 index 00000000..ec78fc1c --- /dev/null +++ b/vpnmanager/vpnmanager-api/src/main/yang/vpn-rpc.yang @@ -0,0 +1,39 @@ +module vpn-rpc { + namespace "urn:opendaylight:vpnservice:vpn:rpc"; + prefix "vpn-rpc"; + + revision "2016-02-01" { + description "VPN Service RPC Module"; + } + + /* RPCs */ + + rpc generate-vpn-label { + description "to generate label for the given ip prefix from the associated VPN"; + input { + leaf vpn-name { + type string; + } + leaf ip-prefix { + type string; + } + } + output { + leaf label { + type uint32; + } + } + } + + rpc remove-vpn-label { + description "to remove label for the given ip prefix from the associated VPN"; + input { + leaf vpn-name { + type string; + } + leaf ip-prefix { + type string; + } + } + } +} \ No newline at end of file -- 2.36.6