/*
* Copyright (c) 2016 Cisco Systems, Inc. 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.groupbasedpolicy.renderer.vpp.nat;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.commons.net.util.SubnetUtils;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.PolicyContext;
import org.opendaylight.groupbasedpolicy.renderer.vpp.util.GbpNetconfTransaction;
import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider;
import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstance;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstanceBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.MappingTable;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.MappingTableBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntryBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.ExternalIpAddressPool;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.renderers.renderer.renderer.nodes.renderer.node.PhysicalInterface;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import javax.annotation.Nullable;
public class NatManager {
private static final Logger LOG = LoggerFactory.getLogger(NatManager.class);
private final Long id;
private final DataBroker dataBroker;
private final MountedDataBrokerProvider mountDataProvider;
public NatManager(DataBroker dataBroker, MountedDataBrokerProvider mountDataProvider) {
this.id = 0L;
this.dataBroker = dataBroker;
this.mountDataProvider = mountDataProvider;
}
public Optional<MappingEntryBuilder> resolveSnatEntry(String internal, Ipv4Address external) {
IpAddress internalIp = null;
LOG.trace("Resolving SNAT entry for internal: {}, external: {}", internal, external);
try {
InetAddress inetAddr = InetAddress.getByName(internal);
if (inetAddr instanceof Inet4Address) {
internalIp = new IpAddress(new Ipv4Address(internal));
} else if (inetAddr instanceof Inet6Address) {
internalIp = new IpAddress(new Ipv6Address(internal));
}
} catch (UnknownHostException e) {
LOG.error("Cannot resolve host IP {}. {}", internal, e.getMessage());
return Optional.absent();
}
SubnetUtils subnet = new SubnetUtils(internal + "/32" );
Long index = Integer.toUnsignedLong(subnet.getInfo().asInteger(internal));
MappingEntryBuilder mappingEntryBuilder =
new MappingEntryBuilder().setType(
org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.MappingEntry.Type.Static)
.setIndex(index)
.setInternalSrcAddress(internalIp)
.setExternalSrcAddress(external);
LOG.trace("Resolved SNAT mapping: {}", mappingEntryBuilder.build().toString());
return Optional.of(mappingEntryBuilder);
}
public void submitNatChanges(final List<InstanceIdentifier<PhysicalInterface>> physIfacesIid,
final @Nullable List<MappingEntryBuilder> sNatEntries,
final PolicyContext policyCtx,
final boolean add) {
if (sNatEntries == null) {
LOG.trace("No static NAT entries to submit");
} else{
LOG.trace("Preparing to submit NAT changes {} on physical interfaces", sNatEntries.toArray(), physIfacesIid);
}
for (InstanceIdentifier<PhysicalInterface> iidPhysIface : physIfacesIid) {
InstanceIdentifier<?> nodeIid = iidPhysIface.firstKeyOf(RendererNode.class).getNodePath();
Optional<DataBroker> mountPointDataBroker = mountDataProvider.getDataBrokerForMountPoint(nodeIid);
if (!mountPointDataBroker.isPresent()) {
throw new IllegalStateException("Cannot find data broker for mount point " + nodeIid);
}
String phInterfaceName = iidPhysIface.firstKeyOf(PhysicalInterface.class).getInterfaceName();
InstanceIdentifier<Interface> interfaceIID =
VppIidFactory.getInterfaceIID(new InterfaceKey(phInterfaceName));
Optional<Interface> readIface =
GbpNetconfTransaction.read(mountPointDataBroker.get(), LogicalDatastoreType.CONFIGURATION, interfaceIID,
GbpNetconfTransaction.RETRY_COUNT);
if (!readIface.isPresent()) {
LOG.error("Interface {} not found on mount point {}", phInterfaceName, nodeIid);
continue;
}
if (add) {
NatInstance natInstance =
buildNatInstance(sNatEntries, NatUtil.resolveDynamicNat(policyCtx, sNatEntries));
GbpNetconfTransaction.netconfSyncedWrite(mountPointDataBroker.get(),
VppIidFactory.getNatInstanceIid(id), natInstance, GbpNetconfTransaction.RETRY_COUNT);
} else {
if (GbpNetconfTransaction.read(mountPointDataBroker.get(), LogicalDatastoreType.CONFIGURATION,
VppIidFactory.getNatInstanceIid(id), GbpNetconfTransaction.RETRY_COUNT).isPresent()) {
GbpNetconfTransaction.netconfSyncedDelete(mountPointDataBroker.get(),
VppIidFactory.getNatInstanceIid(id), GbpNetconfTransaction.RETRY_COUNT);
}
}
}
}
private NatInstance buildNatInstance(List<MappingEntryBuilder> natEntries,
List<ExternalIpAddressPool> poolEntries) {
AtomicInteger ai = new AtomicInteger();
List<MappingEntry> mappingEntries = natEntries.stream().map(me -> {
int value = ai.get();
ai.incrementAndGet();
return me.setIndex((long) value).build();
}).collect(Collectors.toList());
MappingTable mappingTable = new MappingTableBuilder().setMappingEntry(mappingEntries).build();
return new NatInstanceBuilder()
.setId(id)
.setExternalIpAddressPool(poolEntries)
.setMappingTable(mappingTable)
.build();
}
}