<artifactId>dhcpservice-api</artifactId>
<version>${vpnservices.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>natservice-impl</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>natservice-impl</artifactId>
+ <version>${vpnservices.version}</version>
+ <classifier>config</classifier>
+ <type>xml</type>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>natservice-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>neutronvpn-api</artifactId>
<bundle>mvn:org.opendaylight.vpnservice/itm-api/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.vpnservice/neutronvpn-api/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.vpnservice/dhcpservice-api/{{VERSION}}</bundle>
+ <bundle>mvn:org.opendaylight.vpnservice/natservice-api/{{VERSION}}</bundle>
</feature>
<feature name='odl-vpnservice-impl' version='${project.version}' description='OpenDaylight :: vpnservice :: impl '>
<feature version='${mdsal.version}'>odl-mdsal-broker</feature>
<bundle>mvn:org.opendaylight.vpnservice/dhcpservice-impl/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.vpnservice/elanmanager-api/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.vpnservice/elanmanager-impl/{{VERSION}}</bundle>
+ <bundle>mvn:org.opendaylight.vpnservice/natservice-impl/{{VERSION}}</bundle>
<!--<bundle>mvn:org.opendaylight.vpnservice.third-party/org.apache.thriftlib/1.1.0-SNAPSHOT</bundle>-->
<bundle>wrap:mvn:org.apache.thrift/libthrift/0.9.1$overwrite=merge&Bundle-Version=0.9.1&Export-Package=*;-noimport:=true;version="0.9.1"</bundle>
<configfile finalname="neutronvpn-impl-default-config.xml">mvn:org.opendaylight.vpnservice/neutronvpn-impl/{{VERSION}}/xml/config</configfile>
<configfile finalname="dhcpservice-impl-default-config.xml">mvn:org.opendaylight.vpnservice/dhcpservice-impl/{{VERSION}}/xml/config</configfile>
<configfile finalname="elanmanager-impl-default-config.xml">mvn:org.opendaylight.vpnservice/elanmanager-impl/{{VERSION}}/xml/config</configfile>
+ <configfile finalname="natservice-impl-default-config.xml">mvn:org.opendaylight.vpnservice/natservice-impl/{{VERSION}}/xml/config</configfile>
</feature>
<feature name='odl-vpnservice-impl-rest' version='${project.version}' description='OpenDaylight :: vpnservice :: impl :: REST '>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.model</groupId>
+ <artifactId>model-flow-base</artifactId>
+ <version>${openflowplugin.version}</version>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+module fib-rpc {\r
+ namespace "urn:opendaylight:vpnservice:fib:rpc";\r
+ prefix "fib-rpc";\r
+\r
+ import ietf-inet-types {\r
+ prefix inet;\r
+ revision-date "2010-09-24";\r
+ }\r
+\r
+ import opendaylight-flow-types {\r
+ prefix offlow;\r
+ revision-date "2013-10-26";\r
+ }\r
+\r
+ revision "2016-01-21" {\r
+ description "FIB Servicer RPC Module";\r
+ }\r
+\r
+ /* RPCs */\r
+\r
+ rpc create-fib-entry {\r
+ description "to install FIB/LFIB/TST routes on specified dpn with given instructions";\r
+ input {\r
+ leaf source-dpid {\r
+ type uint64;\r
+ }\r
+ leaf vpn-name {\r
+ type string;\r
+ }\r
+ leaf service-id {\r
+ type uint32;\r
+ }\r
+ leaf ip-address {\r
+ type string;\r
+ }\r
+ uses offlow:instruction-list;\r
+ }\r
+ }\r
+\r
+ rpc remove-fib-entry {\r
+ description "to remove FIB/LFIB/TST routes from specified dpn";\r
+ input {\r
+ leaf source-dpid {\r
+ type uint64;\r
+ }\r
+ leaf vpn-name {\r
+ type string;\r
+ }\r
+ leaf service-id {\r
+ type uint32;\r
+ }\r
+ leaf ip-address {\r
+ type string;\r
+ }\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.";
+}
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;
private ItmRpcService itmManager;
private OdlInterfaceRpcService interfaceManager;
private FibNodeCapableListener fibNcListener;
+ private RpcProviderRegistry rpcProviderRegistry;
+ private RpcRegistration<FibRpcService> rpcRegistration;
@Override
public void onSessionInitiated(ProviderContext session) {
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);
}
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;
+ }
+
}
--- /dev/null
+/*
+ * 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<RpcResult<Void>> 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<Instruction> instructions = input.getInstruction();
+
+ makeLocalFibEntry(vpnId, dpnId, ipAddress, instructions);
+ updateVpnToDpnAssociation(vpnId, dpnId, ipAddress, vpnName);
+
+ return Futures.immediateFuture(RpcResultBuilder.<Void>success().build());
+ }
+
+ /**
+ * to remove FIB/LFIB/TST routes from specified dpn
+ *
+ */
+ public Future<RpcResult<Void>> 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.<Void>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<MatchInfo> matches = new ArrayList<MatchInfo>();
+
+ 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+ // 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<Instruction> customInstructions) {
+ List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+
+ 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<Instruction> 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+
+ 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<Instruction> customInstructions) {
+ List<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x8847L }));
+ matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+ List<Instruction> instructions = new ArrayList<Instruction>();
+ List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
+ Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
+ 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<VpnToDpnList> id = getVpnToDpnListIdentifier(rd, dpnId);
+ Optional<VpnToDpnList> 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<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
+ .vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses> 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<VpnToDpnList> id = getVpnToDpnListIdentifier(rd, dpnId);
+ Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+ if (dpnInVpn.isPresent()) {
+ List<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
+ .vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses> 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> 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<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+ = getVpnInstanceToVpnIdIdentifier(vpnName);
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+ = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+ String rd = null;
+ if(vpnInstance.isPresent()) {
+ rd = vpnInstance.get().getVrfId();
+ }
+ return rd;
+ }
+
+ static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
+ 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<VpnToDpnList> 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<VpnInstanceOpDataEntry> 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 <T extends DataObject> void delete(DataBroker broker, LogicalDatastoreType datastoreType,
+ InstanceIdentifier<T> path) {
+ WriteTransaction tx = broker.newWriteOnlyTransaction();
+ tx.delete(datastoreType, path);
+ tx.submit();
+ }
+
+ static long getVpnId(DataBroker broker, String vpnName) {
+
+ InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+ = getVpnInstanceToVpnIdIdentifier(vpnName);
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+ = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+ long vpnId = -1;
+ if(vpnInstance.isPresent()) {
+ vpnId = vpnInstance.get().getVpnId();
+ }
+ return vpnId;
+ }
+
+}
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;
return null;
}
+ @Override
+ public List<IpAddresses> getIpAddresses() { return null; }
+
@Override
public VpnToDpnListKey getKey() {
return new VpnToDpnListKey(Dpn);
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>config-parent</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ <relativePath>../../commons/config-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>natservice-api</artifactId>
+ <version>${vpnservices.version}</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal.model</groupId>
+ <artifactId>ietf-yang-types-20130715</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+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; }
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: --><!--
+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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <parent>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>config-parent</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ <relativePath>../../commons/config-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>natservice-impl</artifactId>
+ <version>${vpnservices.version}</version>
+ <packaging>bundle</packaging>
+ <properties>
+ <powermock.version>1.6.4</powermock.version>
+ <mockitoall.version>1.10.19</mockitoall.version>
+</properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>natservice-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>mdsalutil-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>itm-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgpmanager-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>vpnmanager-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>fibmanager-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>idmanager-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>interfacemgr-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>neutronvpn-api</artifactId>
+ <version>${vpnservices.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ <version>${openflowplugin.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ </dependency>
+ <!-- Only for unit-test -->
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>${mockitoall.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+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
+-->
+<snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:natservice:impl?module=natservice-impl&revision=2016-01-11</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&revision=2015-04-10</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:bgpmanager:api?module=bgpmanager-api&revision=2015-04-20</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&revision=2015-04-10</capability>
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:natservice:impl">prefix:natservice-impl</type>
+ <name>natservice-default</name>
+ <broker>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <name>binding-osgi-broker</name>
+ </broker>
+ <rpc-registry>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+ <bgpmanager>
+ <type xmlns:bgpmanager="urn:opendaylight:params:xml:ns:yang:bgpmanager:api">bgpmanager:bgpmanager-api</type>
+ <name>bgpmanager</name>
+ </bgpmanager>
+ <notification-service>
+ <type xmlns:bindingimpl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">bindingimpl:binding-new-notification-service</type>
+ <name>binding-notification-adapter</name>
+ </notification-service>
+ <mdsalutil>
+ <type xmlns:mdsalutil="urn:opendaylight:params:xml:ns:yang:mdsalutil:api">mdsalutil:odl-mdsalutil</type>
+ <name>mdsalutil-service</name>
+ </mdsalutil>
+ </module>
+ </modules>
+ </data>
+ </configuration>
+</snapshot>
--- /dev/null
+/*
+ * 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<Routers> id = NatUtil.buildRouterIdentifier(routerId);
+ Optional<Routers> 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> 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> 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<Routers> id = NatUtil.buildRouterIdentifier(routerId);
+ Optional<Routers> 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
--- /dev/null
+/*
+ * 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<NAPTEntryEvent> eventQueue;
+ private NaptEventHandler naptEventHandler;
+ private static final Logger LOG = LoggerFactory.getLogger(NaptManager.class);
+
+ EventDispatcher(BlockingQueue<NAPTEntryEvent> 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();
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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<Networks> implements AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(ExternalNetworkListener.class);
+ private ListenerRegistration<DataChangeListener> 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<Networks> getWildCardPath() {
+ return InstanceIdentifier.create(ExternalNetworks.class).child(Networks.class);
+ }
+
+ public void setMdsalManager(IMdsalApiManager mdsalManager) {
+ this.mdsalManager = mdsalManager;
+ }
+
+ @Override
+ protected void add(final InstanceIdentifier<Networks> identifier,
+ final Networks nw) {
+ LOG.trace("External Network add mapping method - key: " + identifier + ", value=" + nw );
+ processExternalNwAdd(identifier, nw);
+ }
+
+ @Override
+ protected void remove(InstanceIdentifier<Networks> identifier, Networks nw) {
+ LOG.trace("External Network remove mapping method - key: " + identifier + ", value=" + nw );
+ processExternalNwDel(identifier, nw);
+ }
+
+ @Override
+ protected void update(InstanceIdentifier<Networks> 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<Uuid> oldRtrs = original.getRouterIds();
+ List<Uuid> 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<Networks> identifier,
+ final Networks network) {
+ LOG.trace("Add event - key: {}, value: {}", identifier, network);
+ List<Uuid> 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<Networks> identifier,
+ final Networks network) {
+ LOG.trace("Add event - key: {}, value: {}", identifier, network);
+ List<Uuid> 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<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerId);
+ Optional<VpnInstanceOpDataEntry> vpnInstOp = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+ if (vpnInstOp.isPresent()) {
+ List<VpnToDpnList> 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ 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
--- /dev/null
+/*
+ * 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<Networks, ExternalNetworksChangeListener>
+{
+ private static final Logger LOG = LoggerFactory.getLogger( ExternalNetworksChangeListener.class);
+
+ private ListenerRegistration<DataChangeListener> 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<DataChangeListener> listenerRegistration) {
+ this.listenerRegistration = listenerRegistration;
+ }
+
+ public ExternalNetworksChangeListener(final DataBroker dataBroker ) {
+ super( Networks.class, ExternalNetworksChangeListener.class );
+ this.dataBroker = dataBroker;
+ }
+
+
+ protected InstanceIdentifier<Networks> getWildCardPath() {
+ return InstanceIdentifier.create(ExternalNetworks.class).child(Networks.class);
+ }
+
+
+ @Override
+ protected void add(InstanceIdentifier<Networks> identifier, Networks networks) {
+
+ }
+
+ @Override
+ protected ExternalNetworksChangeListener getDataTreeChangeListener() {
+ return ExternalNetworksChangeListener.this;
+ }
+
+ @Override
+ protected void remove(InstanceIdentifier<Networks> 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<RouterToNaptSwitch> routerToNaptSwitchInstanceIdentifier =
+ getRouterToNaptSwitchInstanceIdentifier( routerName);
+
+ MDSALUtil.syncDelete( dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitchInstanceIdentifier );
+
+ LOG.debug( "ExternalNetworksChangeListener:delete:: successful deletion of data in napt-switches container" );
+ }
+ }
+
+ private static InstanceIdentifier<RouterToNaptSwitch> 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<Networks> 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<Uuid> routerUuids = update.getRouterIds();
+ for(Uuid routerUuid :routerUuids){
+ InstanceIdentifier<Routers> routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child
+ (Routers.class, new RoutersKey(routerUuid.getValue())).build();
+ Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInstanceIndentifier);
+ if(!routerData.isPresent()){
+ continue;
+ }
+ String routerName = routerData.get().getRouterName();
+ List<String> externalIps = routerData.get().getExternalIps();
+ InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName);
+ Optional<RouterToNaptSwitch> 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<Uuid> routerUuids = original.getRouterIds();
+ for(Uuid routerUuid :routerUuids){
+ InstanceIdentifier<Routers> routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child
+ (Routers.class, new RoutersKey(routerUuid.getValue())).build();
+ Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInstanceIndentifier);
+ List<String> externalIps = null;
+ if(!routerData.isPresent()){
+ continue;
+ }
+ externalIps = routerData.get().getExternalIps();
+ externalRouterListener.handleDisableSnat(routerUuid.getValue(), networkUuid, externalIps);
+ }
+ }
+
+ private void associateExternalNetworkWithVPN(Networks network) {
+ List<Uuid> routerIds = network.getRouterIds();
+ for(Uuid routerId : routerIds) {
+ //long router = NatUtil.getVpnId(dataBroker, routerId.getValue());
+
+ InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerId.getValue());
+ Optional<RouterPorts> 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<Ports> 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> 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> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerId.getValue());
+ Optional<RouterToNaptSwitch> 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<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping> 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<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping> id = idBuilder.build();
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping> ipMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+ if (ipMapping.isPresent()) {
+ List<IpMap> 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<Uuid> routerIds = network.getRouterIds();
+
+ //long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
+ for(Uuid routerId : routerIds) {
+ //long router = NatUtil.getVpnId(dataBroker, routerId.getValue());
+
+ InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerId.getValue());
+ Optional<RouterPorts> 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<Ports> 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> 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<RpcResult<GetDpidFromInterfaceOutput>>
+ dpIdOutput =
+ interfaceManagerRpcService.getDpidFromInterface(dpIdInput);
+ RpcResult<GetDpidFromInterfaceOutput> 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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<Routers, ExternalRoutersListener>{
+
+ private static final Logger LOG = LoggerFactory.getLogger( ExternalRoutersListener.class);
+ private static long label;
+ private ListenerRegistration<DataChangeListener> listenerRegistration;
+ private final DataBroker dataBroker;
+ private IMdsalApiManager mdsalManager;
+ private ItmRpcService itmManager;
+ private OdlInterfaceRpcService interfaceManager;
+ private IdManagerService idManager;
+ private NaptManager naptManager;
+ private NAPTSwitchSelector naptSwitchSelector;
+ private IBgpManager bgpManager;
+ private VpnRpcService vpnService;
+ private FibRpcService fibService;
+ private SNATDefaultRouteProgrammer defaultRouteProgrammer;
+ private 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<Routers> 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<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
+ for(BigInteger dpnId : switches) {
+ // Handle switches and NAPT switches separately
+ if( dpnId != primarySwitchId ) {
+ LOG.debug("NAT Service : Handle Ordinary switch");
+ handleSwitches(dpnId, routerName, primarySwitchId);
+ } else {
+ LOG.debug("NAT Service : Handle NAPT switch");
+ handlePrimaryNaptSwitch(dpnId, routerName, primarySwitchId);
+ }
+ }
+
+ // call registerMapping Api
+ long segmentId = NatUtil.getVpnId(dataBroker, routerName);
+ LOG.debug("NAT Service : Preparing to call registerMapping for routerName {} and Id {}", routerName, segmentId);
+
+ List<Uuid> subnetList = null;
+ List<String> externalIps = null;
+
+ InstanceIdentifier<Routers> id = InstanceIdentifier
+ .builder(ExtRouters.class)
+ .child(Routers.class, new RoutersKey(routerName))
+ .build();
+
+ Optional<Routers> 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<Subnetmap> subnetmapId = InstanceIdentifier
+ .builder(Subnetmaps.class)
+ .child(Subnetmap.class, new SubnetmapKey(subnet))
+ .build();
+ Optional<Subnetmap> 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<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerName);
+ Optional<VpnInstanceOpDataEntry> vpnInstOp = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+ if (vpnInstOp.isPresent()) {
+ List<VpnToDpnList> dpnListInVpn = vpnInstOp.get().getVpnToDpnList();
+ if(dpnListInVpn == null) {
+ LOG.debug("Current no dpns part of router {} to program default NAT route", routerName);
+ return;
+ }
+ 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 <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
+ {
+ ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
+
+ Optional<T> 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x0800L }));
+ matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+ BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ 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<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
+ .setSourceDpid(srcDpId)
+ .setDestinationDpid(dstDpId).build());
+ RpcResult<GetTunnelInterfaceNameOutput> 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<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
+ LOG.debug("NAT Service : getEgressActionsForInterface called for interface {}", ifName);
+ List<ActionInfo> listActionInfo = new ArrayList<ActionInfo>();
+ try {
+ Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
+ interfaceManager.getEgressActionsForInterface(
+ new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
+ RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
+ if(!rpcResult.isSuccessful()) {
+ LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", ifName, rpcResult.getErrors());
+ } else {
+ List<Action> actions =
+ rpcResult.getResult().getAction();
+ for (Action action : actions) {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
+ if (actionClass instanceof OutputActionCase) {
+ listActionInfo.add(new ActionInfo(ActionType.output,
+ new String[] {((OutputActionCase)actionClass).getOutputAction()
+ .getOutputNodeConnector().getValue()}));
+ } else if (actionClass instanceof PushVlanActionCase) {
+ listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
+ } else if (actionClass instanceof SetFieldCase) {
+ if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
+ int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch().getVlanId().getVlanId().getValue();
+ listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
+ new String[] { Long.toString(vlanVid) }));
+ }
+ }
+ }
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.warn("Exception when egress actions for interface {}", ifName, e);
+ }
+ return listActionInfo;
+ }
+
+ protected void installSnatMissEntry(BigInteger dpnId, List<BucketInfo> 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x0800L }));
+ matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+ BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ List<ActionInfo> actionsInfo = new ArrayList<ActionInfo>();
+
+ 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x0800L }));
+ matches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {routerId }));
+
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ 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<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
+ RpcResult<AllocateIdOutput> 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<RpcResult<Void>> 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<ActionInfo> listActionInfoPrimary = new ArrayList<>();
+ String ifNamePrimary = getTunnelInterfaceName( dpnId, primarySwitchId);
+ List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+ 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<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+ List<ActionInfo> listActionInfoPrimary = new ArrayList<ActionInfo>();
+ 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x0800L }));
+ matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+ BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+ ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
+ ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
+ listActionInfo.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) }));
+ instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
+
+ String flowRef = getFlowRefTs(dpId, NatConstants.NAPT_PFIB_TABLE, routerId);
+ 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<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
+
+ //On successful generation of the VPN label, advertise the route to the BGP and install the FIB routes.
+ ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> 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<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
+
+ for (IpMap dbIpMap : dbIpMaps) {
+ String dbExternalIp = dbIpMap.getExternalIp();
+ //Select the IPMap, whose external IP is the IP for which FIB is installed
+ if (externalIp.contains(dbExternalIp)) {
+ String dbInternalIp = dbIpMap.getInternalIp();
+ IpMapKey dbIpMapKey = dbIpMap.getKey();
+ IpMap newIpm = new IpMapBuilder().setKey(dbIpMapKey).setInternalIp(dbInternalIp).setExternalIp(dbExternalIp).setLabel(label).build();
+ MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, naptManager.getIpMapIdentifier(routerId, dbInternalIp), newIpm);
+ }
+ }
+
+ //Install custom FIB routes
+ List<Instruction> customInstructions = new ArrayList<>();
+ customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(0));
+ makeTunnelTableEntry(dpnId, label, customInstructions);
+ makeLFibTableEntry(dpnId, label, customInstructions);
+
+ CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId)
+ .setIpAddress(externalIp).setServiceId(label).setInstruction(customInstructions).build();
+ Future<RpcResult<Void>> 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<RpcResult<Void>>() {
+
+ @Override
+ public void onFailure(Throwable error) {
+ log.error("NAT Service : Error in generate label or fib install process", error);
+ }
+
+ @Override
+ public void onSuccess(RpcResult<Void> 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<Instruction> customInstructions) {
+ List<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x8847L }));
+ matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+ List<Instruction> instructions = new ArrayList<Instruction>();
+ List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
+ Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
+ 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<Instruction> customInstructions) {
+ List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+
+ 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<RouterIds> getRoutersIdentifier(long routerId) {
+ InstanceIdentifier<RouterIds> 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<Routers> 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<String> 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<Routers> 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<String> externalIps = router.getExternalIps();
+ handleDisableSnat(routerName, networkUuid, externalIps);
+ }
+ }
+
+ public void handleDisableSnat(String routerName, Uuid networkUuid, List<String> externalIps){
+ LOG.info("NAT Service : handleDisableSnat() Entry");
+ Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+
+ BigInteger naptSwitchDpnId = null;
+ InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName);
+ Optional<RouterToNaptSwitch> rtrToNapt = read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch );
+ if(rtrToNapt.isPresent()) {
+ naptSwitchDpnId = rtrToNapt.get().getPrimarySwitchId();
+ }
+ LOG.debug("NAT Service : got primarySwitch as dpnId{} ", naptSwitchDpnId);
+
+ 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<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+ 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<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType();
+ for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){
+ List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
+ for(IpPortMap ipPortMap : ipPortMaps){
+ String ipPortInternal = ipPortMap.getIpPortInternal();
+ String[] ipPortParts = ipPortInternal.split(":");
+ if(ipPortParts.length != 2) {
+ LOG.error("NAT Service : Unable to retrieve the Internal IP and port");
+ return;
+ }
+ String internalIp = ipPortParts[0];
+ String internalPort = ipPortParts[1];
+
+ //Build the flow for the outbound NAPT table
+ String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId), internalIp, Integer.valueOf(internalPort));
+ FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+
+ LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+ mdsalManager.removeFlow(outboundNaptFlowEntity);
+
+ IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
+ String externalIp = ipPortExternal.getIpAddress();
+ int externalPort = ipPortExternal.getPortNum();
+
+ //Build the flow for the inbound NAPT table
+ switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
+ FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+
+ LOG.info("NAT Service : Remove the flow in the " + NatConstants.INBOUND_NAPT_TABLE + " for the active active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+ mdsalManager.removeFlow(inboundNaptFlowEntity);
+ }
+ }
+ }
+
+ 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<VpnToDpnList> 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<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+ 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<String> 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<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
+ final String externalIp = externalIps.get(0);
+
+ //Get IPMaps from the DB for the router ID
+ List<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
+ if(dbIpMaps == null ){
+ LOG.error("NAT Service : IPMaps is null");
+ 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<RpcResult<Void>> future = fibService.removeFibEntry(input);
+
+ ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
+ //Release label
+ if (result.isSuccessful()) {
+ removeTunnelTableEntry(dpnId, label);
+ removeLFibTableEntry(dpnId, label);
+ RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+ Future<RpcResult<Void>> 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<RpcResult<Void>>() {
+
+ @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<Void> 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<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+ // 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<Routers> getWildCardPath()
+ {
+ return InstanceIdentifier.create(ExtRouters.class).child(Routers.class);
+ }
+
+ @Override
+ protected ExternalRoutersListener getDataTreeChangeListener()
+ {
+ return ExternalRoutersListener.this;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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<IpMapping> implements AutoCloseable{
+ private static final Logger LOG = LoggerFactory.getLogger(FloatingIPListener.class);
+ private ListenerRegistration<DataChangeListener> 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<IpMapping> 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<IpMapping> identifier,
+ final IpMapping mapping) {
+ LOG.trace("FloatingIPListener add ip mapping method - key: " + identifier + ", value=" + mapping );
+ processFloatingIPAdd(identifier, mapping);
+ }
+
+ @Override
+ protected void remove(InstanceIdentifier<IpMapping> identifier, IpMapping mapping) {
+ LOG.trace("FloatingIPListener remove ip mapping method - key: " + identifier + ", value=" + mapping );
+ processFloatingIPDel(identifier, mapping);
+ }
+
+ @Override
+ protected void update(InstanceIdentifier<IpMapping> 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<RpcResult<GetDpidFromInterfaceOutput>>
+ dpIdOutput =
+ interfaceManagerRpcService.getDpidFromInterface(dpIdInput);
+ RpcResult<GetDpidFromInterfaceOutput> 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ actionsInfos.add(new ActionInfo(ActionType.set_destination_ip, new String[]{ internalIp, "32" }));
+
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+// actionsInfos.add(new ActionInfo(ActionType.set_destination_ip, new String[]{ internalIp, "32" }));
+
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+// 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ actionsInfos.add(new ActionInfo(ActionType.set_source_ip, new String[]{ externalIp, "32" }));
+
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+// 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<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ //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<RouterPorts> pIdentifier) {
+ Optional<RouterPorts> 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<Networks> nwId = InstanceIdentifier.builder(ExternalNetworks.class).child(Networks.class, new NetworksKey(extNwId)).build();
+ Optional<Networks> 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<IpMapping> 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<RouterPorts> pIdentifier = identifier.firstIdentifierOf(RouterPorts.class);
+ createNATFlowEntries(interfaceName, mapping, pIdentifier, routerId);
+ }
+
+ private void processFloatingIPDel(final InstanceIdentifier<IpMapping> 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<RouterPorts> 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<RouterPorts> 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<Routers> routersIdentifier = NatUtil.buildRouterIdentifier(routerName);
+ Optional<Routers> 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<RouterPorts> 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<IpMapping> ipMappingIdentifier = NatUtil.getIpMappingIdentifier(routerId, interfaceName, internalIp);
+ Optional<IpMapping> ipMapping = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, ipMappingIdentifier);
+ if(ipMapping.isPresent()) {
+ return ipMapping.get().getLabel();
+ }
+ return NatConstants.INVALID_ID;
+ }
+
+ private Uuid getExternalNetworkForRouter(String routerId) {
+ InstanceIdentifier<RouterPorts> identifier = NatUtil.getRouterPortsId(routerId);
+ Optional<RouterPorts> 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<Ports> portsId = NatUtil.getPortsIdentifier(routerId, interfaceName);
+ Optional<Ports> 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<IpMapping> 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<IpMapping> 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;
+
+
+ }
+}
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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<Interface> implements AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(InterfaceStateEventListener.class);
+ private ListenerRegistration<DataChangeListener> 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<Interface> getWildCardPath() {
+ return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
+ }
+
+ @Override
+ protected void remove(InstanceIdentifier<Interface> 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<Interface> 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<Interface> 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<String> 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<ProtocolTypes> 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<Integer> 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<ProtocolTypes> getPortocolList() {
+ List<ProtocolTypes> protocollist = Lists.newArrayList();
+ protocollist.add(ProtocolTypes.TCP);
+ protocollist.add(ProtocolTypes.UDP);
+ return protocollist;
+ }
+
+ private BigInteger getNaptSwitchforRouter(DataBroker broker,String routerName) {
+ InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class).child
+ (RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
+ Optional<RouterToNaptSwitch> 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<IpMapping> ipMappingList = getIpMappingForPortName(portName, routerId);
+ if (ipMappingList == null || ipMappingList.isEmpty()) {
+ LOG.trace("Ip Mapping list is empty/null for portname {}", portName);
+ return;
+ }
+ InstanceIdentifier<RouterPorts> 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<IpMapping> ipMappingList = getIpMappingForPortName(portName, routerId);
+ if (ipMappingList == null || ipMappingList.isEmpty()) {
+ LOG.trace("Ip Mapping list is empty/null for portName {}", portName);
+ return;
+ }
+ InstanceIdentifier<RouterPorts> pIdentifier = NatUtil.buildRouterPortsIdentifier(routerId);
+ for (IpMapping ipMapping : ipMappingList) {
+ floatingIPListener.removeNATFlowEntries(portName, ipMapping, pIdentifier, routerId);
+ }
+ }
+
+ private List<IpMapping> getIpMappingForPortName(String portName, String routerId) {
+ InstanceIdentifier<Ports> portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
+ Optional<Ports> 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<IpMapping> ipMappingList = port.get().getIpMapping();
+ return ipMappingList;
+ }
+
+ private List<String> getFixedIpsForPort (String interfname) {
+ LOG.debug("getFixedIpsForPort method is called for interface {}",interfname);
+ try {
+ Future<RpcResult<GetFixedIPsForNeutronPortOutput>> result =
+ neutronVpnService.getFixedIPsForNeutronPort(new GetFixedIPsForNeutronPortInputBuilder()
+ .setPortId(new Uuid(interfname)).build());
+
+ RpcResult<GetFixedIPsForNeutronPortOutput> 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
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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<BigInteger, Integer> naptSwitchWeights = constructNAPTSwitches();
+ List<BigInteger> 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<SwitchWeight> 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<SwitchWeight> it = switchWeights.iterator();
+ List<RouterToNaptSwitch> 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<BigInteger, Integer> constructNAPTSwitches() {
+ Optional<NaptSwitches> optNaptSwitches = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier());
+ Map<BigInteger, Integer> switchWeights = new HashMap<>();
+
+ if(optNaptSwitches.isPresent()) {
+ NaptSwitches naptSwitches = optNaptSwitches.get();
+ List<RouterToNaptSwitch> 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<NaptSwitches> getNaptSwitchesIdentifier() {
+ return InstanceIdentifier.create(NaptSwitches.class);
+ }
+
+ public List<BigInteger> getDpnsForVpn(String routerName ) {
+ LOG.debug( "getVpnToDpnList called for RouterName {}", routerName );
+
+ InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.builder(VpnInstanceOpData.class)
+ .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(routerName))
+ .build();
+
+ List<BigInteger> dpnsInVpn = new ArrayList<>();
+ Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+
+ if(vpnInstanceOpData.isPresent()) {
+ LOG.debug( "NATService : getVpnToDpnList able to fetch vpnInstanceOpData" );
+ VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnInstanceOpData.get();
+ List<VpnToDpnList> 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<SwitchWeight>
+ {
+ 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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<MatchInfo> 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> 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<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, String port, long routerId, long vpnId, short tableId, NAPTEntryEvent.Protocol protocol) {
+ ActionInfo ipActionInfo = null;
+ ActionInfo portActionInfo = null;
+ ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
+ ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
+
+ if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
+ ipActionInfo = new ActionInfo(ActionType.set_source_ip, new String[] {ipAddress});
+ if(protocol == NAPTEntryEvent.Protocol.TCP) {
+ portActionInfo = new ActionInfo( ActionType.set_tcp_source_port, new String[] {port});
+ } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
+ portActionInfo = new ActionInfo( ActionType.set_udp_source_port, new String[] {port});
+ }
+ instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID}));
+ }else{
+ ipActionInfo = new ActionInfo(ActionType.set_destination_ip, new String[] {ipAddress});
+ if(protocol == NAPTEntryEvent.Protocol.TCP) {
+ portActionInfo = new ActionInfo( ActionType.set_tcp_destination_port, new String[] {port});
+ } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
+ portActionInfo = new ActionInfo( ActionType.set_udp_destination_port, new String[] {port});
+ }
+ instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(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);
+
+ }
+
+}
--- /dev/null
+/*
+ * 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<Node> 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
+
+ }
+
+}
--- /dev/null
+/*
+ * 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<RpcResult<Void>> 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<String> allIps = new ArrayList<String>();
+ 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<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
+ RpcResult<AllocateIdOutput> 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<Integer> 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<IpMap> getIpMapIdentifier(long segid, String internal) {
+ InstanceIdentifier<IpMap> id = InstanceIdentifier.builder(
+ IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segid)).child(IpMap.class, new IpMapKey(internal)).build();
+ return id;
+ }
+
+ public static List<IpMap> getIpMapList(DataBroker broker, Long routerId) {
+ InstanceIdentifier id = getIpMapList(routerId);
+ Optional<IpMapping> ipMappingListData = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+ if (ipMappingListData.isPresent()) {
+ IpMapping ipMapping = ipMappingListData.get();
+ return ipMapping.getIpMap();
+ }
+ return null;
+ }
+
+ protected static InstanceIdentifier<IpMapping> getIpMapList(long routerId) {
+ InstanceIdentifier<IpMapping> id = InstanceIdentifier.builder(
+ IntextIpMap.class).child(IpMapping.class, new IpMappingKey(routerId)).build();
+ return id;
+ }
+
+ protected InstanceIdentifier<IpPortMap> getIpPortMapIdentifier(long segid, String internal, NAPTEntryEvent.Protocol protocol) {
+ ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
+ InstanceIdentifier<IpPortMap> 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<IntextIpProtocolType> idBuilder =
+ InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(segmentId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType));
+ InstanceIdentifier<IntextIpProtocolType> id = idBuilder.build();
+ Optional<IntextIpProtocolType> intextIpProtocolType = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+ if (intextIpProtocolType.isPresent()) {
+ List<IpPortMap> 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<IpMapping> idBuilder =
+ InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segmentId));
+ InstanceIdentifier<IpMapping> id = idBuilder.build();
+ Optional<IpMapping> ipMapping = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+ if (ipMapping.isPresent()) {
+ List<IpMap> 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<Integer> 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<IpPort> 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<IpPortMap> idBuilder = InstanceIdentifier.builder(IntextIpPortMap.class)
+ .child(IpPortMapping.class, new IpPortMappingKey(segmentId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
+ .child(IpPortMap.class, new IpPortMapKey(internalIpPort));
+ InstanceIdentifier<IpPortMap> 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<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
+ .child(IpMapping.class, new IpMappingKey(segmentId))
+ .child(IpMap.class, new IpMapKey(internalIp));
+ InstanceIdentifier<IpMap> 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<IpMapping> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
+ .child(IpMapping.class, new IpMappingKey(segmentId));
+ InstanceIdentifier<IpMapping> 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<RpcResult<Void>> result = idManager.releaseId(idInput);
+ RpcResult<Void> 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
--- /dev/null
+/*
+ * 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<String> 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
--- /dev/null
+/*
+ * 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<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
+ NatUtil.buildRouterIdentifier(routerName));
+ if(routerData.isPresent()){
+ List<String> 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<IpMapping> ipMappingOptional = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+ getIpMappingBuilder(routerId));
+ if (vpnName != null) {
+ if (ipMappingOptional.isPresent()) {
+ List<IpMap> 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<IpMapping> getIpMappingBuilder(Long routerId) {
+ InstanceIdentifier<IpMapping> 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<BigInteger> 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<BucketInfo> bucketInfoList = handleGroupInPrimarySwitch();
+ modifySnatGroupEntry(naptSwitch, bucketInfoList, routerName);
+ } else {
+ LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is not naptSwitch for router {}"
+ , dpn, routerName);
+ List<BucketInfo> 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<BigInteger> getDpnListForRouter(String routerName) {
+ List<BigInteger> dpnList = new ArrayList<BigInteger>();
+ List<VpnToDpnList> 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<Node> nodeInstanceId = InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class, new NodeKey(nodeId)).build();
+ Optional<Node> 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<BucketInfo> handleGroupInPrimarySwitch() {
+ List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+ List<ActionInfo> listActionInfoPrimary = new ArrayList<ActionInfo>();
+ 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<BucketInfo> handleGroupInNeighborSwitches(BigInteger dpnId, String routerName, BigInteger naptSwitch) {
+ List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+ 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<ActionInfo> 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> 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> 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<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(
+ new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId).setDestinationDpid(dstDpId).build());
+ RpcResult<GetTunnelInterfaceNameOutput> 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<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
+ LOG.debug("getEgressActionsForInterface called for interface {}", ifName);
+ List<ActionInfo> listActionInfo = new ArrayList<ActionInfo>();
+ try {
+ Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
+ interfaceManager.getEgressActionsForInterface(
+ new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
+ RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
+ if(!rpcResult.isSuccessful()) {
+ LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}"
+ , ifName, rpcResult.getErrors());
+ } else {
+ List<Action> actions =
+ rpcResult.getResult().getAction();
+ for (Action action : actions) {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
+ if (actionClass instanceof OutputActionCase) {
+ listActionInfo.add(new ActionInfo(ActionType.output,
+ new String[] {((OutputActionCase)actionClass).getOutputAction()
+ .getOutputNodeConnector().getValue()}));
+ } else if (actionClass instanceof PushVlanActionCase) {
+ listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
+ } else if (actionClass instanceof SetFieldCase) {
+ if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
+ int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch()
+ .getVlanId().getVlanId().getValue();
+ listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
+ new String[] { Long.toString(vlanVid) }));
+ }
+ }
+ }
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.warn("Exception when egress actions for interface {}", ifName, e);
+ }
+ return listActionInfo;
+ }
+
+ private IpPortMapping getIpPortMapping(Long routerId) {
+ Optional<IpPortMapping> 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<IpPortMapping> buildIpToPortMapIdentifier(Long routerId) {
+ InstanceIdentifier<IpPortMapping> 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[]{ 0x0800L }));
+ matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+ BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+ String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
+
+ if (addordel == NatConstants.ADD_FLOW) {
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ List<ActionInfo> actionsInfo = new ArrayList<ActionInfo>();
+
+ 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
--- /dev/null
+/*
+ * 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;
+
+}
--- /dev/null
+/*
+ * 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<Node> implements AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(NatNodeEventListener.class);
+ private ListenerRegistration<DataChangeListener> 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<Node> getWildCardPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class);
+ }
+
+ @Override
+ protected void remove(InstanceIdentifier<Node> 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<Node> identifier, Node original, Node update) {
+ LOG.trace("NatNodeEventListener: Node update received");
+ }
+
+ @Override
+ protected void add(InstanceIdentifier<Node> 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");
+ }
+}
--- /dev/null
+/*
+ * 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<NAPTEntryEvent> 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+ = getVpnInstanceToVpnIdIdentifier(vpnName);
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+ = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+ long vpnId = NatConstants.INVALID_ID;
+ if(vpnInstance.isPresent()) {
+ vpnId = vpnInstance.get().getVpnId();
+ }
+ return vpnId;
+ }
+
+ static InstanceIdentifier<RouterPorts> getRouterPortsId(String routerId) {
+ return InstanceIdentifier.builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerId)).build();
+ }
+
+ static InstanceIdentifier<Ports> 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<IpMapping> 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<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
+ 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<Routers> routerData = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+ if (routerData.isPresent()) {
+ return routerData.get().getNetworkId();
+ }
+ return null;
+ }
+
+ static InstanceIdentifier<Routers> buildRouterIdentifier(String routerId) {
+ InstanceIdentifier<Routers> 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<Routers> 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<Networks> networkData = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+ if (networkData.isPresent()) {
+ return networkData.get().getVpnid();
+ }
+ return null;
+ }
+
+ private static InstanceIdentifier<Networks> buildNetworkIdentifier(Uuid networkId) {
+ InstanceIdentifier<Networks> 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<RouterToNaptSwitch> routerToNaptSwitchData = read(broker, LogicalDatastoreType.OPERATIONAL, id);
+ if (routerToNaptSwitchData.isPresent()) {
+ RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get();
+ return routerToNaptSwitchInstance.getPrimarySwitchId();
+ }
+ return null;
+ }
+
+ private static InstanceIdentifier<RouterToNaptSwitch> buildNaptSwitchIdentifier(String routerId) {
+ InstanceIdentifier<RouterToNaptSwitch> 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<RouterIds> routerIdsData = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+ if (routerIdsData.isPresent()) {
+ RouterIds routerIdsInstance = routerIdsData.get();
+ return routerIdsInstance.getRouterName();
+ }
+ return null;
+ }
+
+ private static InstanceIdentifier<RouterIds> buildRouterIdentifier(Long routerId) {
+ InstanceIdentifier<RouterIds> routerIds = InstanceIdentifier.builder(RouterIdName.class).child
+ (RouterIds.class, new RouterIdsKey(routerId)).build();
+ return routerIds;
+ }
+
+ public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
+ InstanceIdentifier<T> path) {
+
+ ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
+
+ Optional<T> result = Optional.absent();
+ try {
+ result = tx.read(datastoreType, path).get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ return result;
+ }
+
+ static InstanceIdentifier<VpnInstanceOpDataEntry> getVpnInstanceOpDataIdentifier(String vrfId) {
+ return InstanceIdentifier.builder(VpnInstanceOpData.class)
+ .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vrfId)).build();
+ }
+
+ public static long readVpnId(DataBroker broker, String vpnName) {
+
+ InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+ = getVpnInstanceToVpnIdIdentifier(vpnName);
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> 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<DPNTEPsInfo> tunnelInfoId =
+ InstanceIdentifier.builder(DpnEndpoints.class).child(DPNTEPsInfo.class, new DPNTEPsInfoKey(dpnId)).build();
+ Optional<DPNTEPsInfo> tunnelInfo = read(broker, LogicalDatastoreType.CONFIGURATION, tunnelInfoId);
+ if (tunnelInfo.isPresent()) {
+ List<TunnelEndPoints> 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<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+ = getVpnInstanceToVpnIdIdentifier(vpnName);
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> 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<IpPortMap> ipPortMapData = read(broker, LogicalDatastoreType.CONFIGURATION, ipPortMapId);
+ if (ipPortMapData.isPresent()) {
+ IpPortMap ipPortMapInstance = ipPortMapData.get();
+ return ipPortMapInstance.getIpPortExternal();
+ }
+ return null;
+ }
+
+ private static InstanceIdentifier<IpPortMap> buildIpToPortMapIdentifier(Long routerId, String internalIpAddress, String internalPort , ProtocolTypes protocolType) {
+ InstanceIdentifier<IpPortMap> 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<MatchInfo> 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<VpnInterface> interfaceId = getVpnInterfaceIdentifier(interfaceName);
+ Optional<VpnInterface> configuredVpnInterface = read(broker, LogicalDatastoreType.CONFIGURATION, interfaceId);
+
+ if (configuredVpnInterface.isPresent()) {
+ return true;
+ }
+ return false;
+ }
+
+ static InstanceIdentifier<VpnInterface> getVpnInterfaceIdentifier(String vpnInterfaceName) {
+ return InstanceIdentifier.builder(VpnInterfaces.class)
+ .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).build();
+ }
+
+ static VpnInterface getConfiguredVpnInterface(DataBroker broker, String interfaceName) {
+ InstanceIdentifier<VpnInterface> interfaceId = getVpnInterfaceIdentifier(interfaceName);
+ Optional<VpnInterface> 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<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
+ .child(VpnMap.class, new VpnMapKey(new Uuid(vpnName))).build();
+ Optional<VpnMap> optionalVpnMap = read(broker, LogicalDatastoreType.CONFIGURATION,
+ vpnMapIdentifier);
+ if (optionalVpnMap.isPresent()) {
+ return optionalVpnMap.get().getRouterId().getValue();
+ }
+ return null;
+ }
+
+
+ public static List<VpnToDpnList> getVpnToDpnList(DataBroker dataBroker, String vrfId )
+ {
+ List<VpnToDpnList> vpnDpnList = null;
+
+ InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier
+ .builder(VpnInstanceOpData.class)
+ .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vrfId))
+ .build();
+
+ Optional<VpnInstanceOpDataEntry> 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<Ports> buildPortToIpMapIdentifier(String routerId, String portName) {
+ InstanceIdentifier<Ports> ipPortMapId = InstanceIdentifier.builder(FloatingIpInfo.class).child
+ (RouterPorts.class, new RouterPortsKey(routerId)).child(Ports.class, new PortsKey(portName)).build();
+ return ipPortMapId;
+ }
+
+ static InstanceIdentifier<RouterPorts> buildRouterPortsIdentifier(String routerId) {
+ InstanceIdentifier<RouterPorts> 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<Integer> getInternalIpPortListInfo(DataBroker dataBroker,Long routerId, String internalIpAddress, ProtocolTypes protocolType){
+ Optional<IntIpProtoType> optionalIpProtoType = read(dataBroker, LogicalDatastoreType.CONFIGURATION, buildSnatIntIpPortIdentifier(routerId, internalIpAddress, protocolType));
+ if (optionalIpProtoType.isPresent()) {
+ return optionalIpProtoType.get().getPorts();
+ }
+ return null;
+ }
+
+ public static InstanceIdentifier<IntIpProtoType> buildSnatIntIpPortIdentifier(Long routerId, String internalIpAddress, ProtocolTypes protocolType) {
+ InstanceIdentifier<IntIpProtoType> 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<NaptSwitches> switchesOptional = read(broker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier());
+ if(switchesOptional.isPresent()) {
+ return switchesOptional.get();
+ }
+ return null;
+ }
+
+ public static InstanceIdentifier<NaptSwitches> getNaptSwitchesIdentifier() {
+ return InstanceIdentifier.create(NaptSwitches.class);
+ }
+
+ public static InstanceIdentifier<RouterToNaptSwitch> 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<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
+ RpcResult<AllocateIdOutput> 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<IpPortMapping> getIportMappingData = read(broker, LogicalDatastoreType.CONFIGURATION, getIportMappingIdentifier(routerId));
+ if(getIportMappingData.isPresent()) {
+ return getIportMappingData.get();
+ }
+ return null;
+ }
+
+ public static InstanceIdentifier<IpPortMapping> getIportMappingIdentifier(long routerId) {
+ return InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(routerId)).build();
+ }
+}
--- /dev/null
+/*
+ * 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<RouterPorts> implements AutoCloseable{
+ private static final Logger LOG = LoggerFactory.getLogger(RouterPortsListener.class);
+ private ListenerRegistration<DataChangeListener> 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<RouterPorts> getWildCardPath() {
+ return InstanceIdentifier.create(FloatingIpInfo.class).child(RouterPorts.class);
+ }
+
+
+ @Override
+ protected void add(final InstanceIdentifier<RouterPorts> identifier, final RouterPorts routerPorts) {
+ LOG.trace("Add router ports method - key: " + identifier + ", value=" + routerPorts );
+ Optional<RouterPorts> 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<RouterPorts> identifier, RouterPorts routerPorts) {
+ LOG.trace("Remove router ports method - key: " + identifier + ", value=" + routerPorts );
+ //MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+
+ }
+
+ @Override
+ protected void update(InstanceIdentifier<RouterPorts> identifier, RouterPorts original, RouterPorts update) {
+ LOG.trace("Update router ports method - key: " + identifier + ", original=" + original + ", update=" + update );
+ }
+}
--- /dev/null
+/*
+ * 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
+
+ ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> 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<Instruction> instructions = new ArrayList<Instruction>();
+ List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ 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<Instruction> 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<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
+ Future<RpcResult<Void>> 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<RpcResult<Void>>() {
+
+ @Override
+ public void onFailure(Throwable error) {
+ LOG.error("Error in generate label or fib install process", error);
+ }
+
+ @Override
+ public void onSuccess(RpcResult<Void> 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<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
+ RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
+ Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
+
+ ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
+ //Release label
+ if(result.isSuccessful()) {
+ removeTunnelTableEntry(dpnId, label);
+ removeLFibTableEntry(dpnId, label);
+ RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+ Future<RpcResult<Void>> 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<RpcResult<Void>>() {
+
+ @Override
+ public void onFailure(Throwable error) {
+ LOG.error("Error in removing the label or custom fib entries", error);
+ }
+
+ @Override
+ public void onSuccess(RpcResult<Void> 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<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
+ RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp).setServiceId(label).build();
+ Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
+
+ ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future),
+ new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
+ //Release label
+ if(result.isSuccessful()) {
+ removeTunnelTableEntry(dpnId, label);
+ removeLFibTableEntry(dpnId, label);
+ RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+ Future<RpcResult<Void>> 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<RpcResult<Void>>() {
+
+ @Override
+ public void onFailure(Throwable error) {
+ LOG.error("Error in removing the label or custom fib entries", error);
+ }
+
+ @Override
+ public void onSuccess(RpcResult<Void> 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<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+ // 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<Instruction> customInstructions) {
+ List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+
+ 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<Instruction> customInstructions) {
+ List<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x8847L }));
+ matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+ List<Instruction> instructions = new ArrayList<Instruction>();
+ List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
+ Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
+ 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<MatchInfo> matches = new ArrayList<MatchInfo>();
+ 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);
+ }
+
+}
+
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+* 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 {
+
+}
--- /dev/null
+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
--- /dev/null
+/*
+ * 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<DataChangeListener> dataChangeListenerRegistration;
+ @Mock IMdsalApiManager mdsalManager;
+ @Mock FlowEntity flowMock;
+ @Mock GroupEntity groupMock;
+ InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks> 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> bucketInfo = new ArrayList<BucketInfo>();
+ List<ActionInfo> listActionInfoPrimary = new ArrayList<ActionInfo>();
+ listActionInfoPrimary.add(new ActionInfo(ActionType.output,
+ new String[] {"3"}));
+ BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+ List<ActionInfo> listActionInfoSecondary = new ArrayList<ActionInfo>();
+ listActionInfoSecondary.add(new ActionInfo(ActionType.output,
+ new String[] {"4"}));
+ BucketInfo bucketSecondary = new BucketInfo(listActionInfoPrimary);
+ bucketInfo.add(0, bucketPrimary);
+ bucketInfo.add(1, bucketSecondary);
+
+ List<MatchInfo> matches = new ArrayList<MatchInfo>();
+ matches.add(new MatchInfo(MatchFieldType.eth_type,
+ new long[] { 0x0800L }));
+
+ List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+ List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+ 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(); */
+
+ }
+
+}
--- /dev/null
+/*
+ * 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<DataChangeListener> dataChangeListenerRegistration;
+ InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap> 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.<Void>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<IpPortMapping> idBuilder =
+ InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(5L));
+ InstanceIdentifier<IpPortMapping> 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<IpMapping> idBuilder =
+ InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(5L));
+ InstanceIdentifier<IpMapping> 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);*/
+ }
+
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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 INTERNAL
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <parent>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>odlparent</artifactId>
+ <version>1.7.0-SNAPSHOT</version>
+ <relativePath/>
+ </parent>
+
+ <groupId>org.opendaylight.vpnservice</groupId>
+ <artifactId>natservice-aggregator</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ <name>natservice</name>
+ <packaging>pom</packaging>
+ <modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.1.1</maven>
+ </prerequisites>
+ <modules>
+ <module>natservice-api</module>
+ <module>natservice-impl</module>
+ </modules>
+ <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
<module>neutronvpn</module>
<module>dhcpservice</module>
<module>itm</module>
+ <module>natservice</module>
<module>distribution/karaf</module>
<module>features</module>
<module>vpnservice-artifacts</module>
type string;
}
}
+ list ip-addresses {
+ key ip-address;
+ leaf ip-address {
+ type string;
+ }
+ }
}
}
}
}
}
+ 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
--- /dev/null
+module vpn-rpc {\r
+ namespace "urn:opendaylight:vpnservice:vpn:rpc";\r
+ prefix "vpn-rpc";\r
+\r
+ revision "2016-02-01" {\r
+ description "VPN Service RPC Module";\r
+ }\r
+\r
+ /* RPCs */\r
+\r
+ rpc generate-vpn-label {\r
+ description "to generate label for the given ip prefix from the associated VPN";\r
+ input {\r
+ leaf vpn-name {\r
+ type string;\r
+ }\r
+ leaf ip-prefix {\r
+ type string;\r
+ }\r
+ }\r
+ output {\r
+ leaf label {\r
+ type uint32;\r
+ }\r
+ }\r
+ }\r
+\r
+ rpc remove-vpn-label {\r
+ description "to remove label for the given ip prefix from the associated VPN";\r
+ input {\r
+ leaf vpn-name {\r
+ type string;\r
+ }\r
+ leaf ip-prefix {\r
+ type string;\r
+ }\r
+ }\r
+ }\r
+}
\ No newline at end of file