/*
* 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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.net.util.SubnetUtils;
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.WriteTransaction;
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.VppIidFactory;
import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
import org.opendaylight.groupbasedpolicy.util.NetUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
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.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
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.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.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.ExternalIpAddressPoolBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.SubnetAugmentRenderer;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.Subnet;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.subnet.AllocationPool;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererNodes;
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.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.RendererForwardingByTenant;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomain;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.VppInterfaceAugmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.renderers.renderer.renderer.nodes.renderer.node.PhysicalInterface;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.renderers.renderer.renderer.nodes.renderer.node.PhysicalInterfaceKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214.NatInterfaceAugmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.Nat;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.InboundBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.OutboundBuilder;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
public class NatUtil {
private static final Logger LOG = LoggerFactory.getLogger(NatUtil.class);
public void setInboundInterface(Interface iface, WriteTransaction wTx) {
InstanceIdentifier<Nat> natIid = buildNatIid(VppIidFactory.getInterfaceIID(iface.getKey()));
Nat nat = new NatBuilder().setInbound(new InboundBuilder().build()).build();
wTx.put(LogicalDatastoreType.CONFIGURATION, natIid, nat);
}
public static void setOutboundInterface(Interface iface, DataBroker dataBroker) {
InstanceIdentifier<Nat> natIid = buildNatIid(VppIidFactory.getInterfaceIID(iface.getKey()));
Nat nat = new NatBuilder().setOutbound(new OutboundBuilder().build()).build();
GbpNetconfTransaction.netconfSyncedWrite(dataBroker, natIid, nat, GbpNetconfTransaction.RETRY_COUNT);
}
public static void unsetOutboundInterface(Interface iface, DataBroker dataBroker) {
InstanceIdentifier<Nat> natIid = buildNatIid(VppIidFactory.getInterfaceIID(iface.getKey()));
GbpNetconfTransaction.netconfSyncedDelete(dataBroker, natIid, GbpNetconfTransaction.RETRY_COUNT);
}
public static InstanceIdentifier<Nat> buildNatIid(InstanceIdentifier<Interface> ifaceIid) {
return ifaceIid.builder().augmentation(NatInterfaceAugmentation.class).child(Nat.class).build();
}
public static Optional<InstanceIdentifier<PhysicalInterface>> resolvePhysicalInterface(IpPrefix extSubnetPrefix,
ReadOnlyTransaction rTx) {
Optional<RendererNodes> readFromDs =
DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, VppIidFactory.getRendererNodesIid(), rTx);
rTx.close();
if (!readFromDs.isPresent() || readFromDs.get().getRendererNode() == null) {
return Optional.absent();
}
RendererNodes rendererNodes = readFromDs.get();
List<RendererNode>
vppNodes =
rendererNodes.getRendererNode()
.stream()
.filter(rn -> rn.getAugmentation(VppInterfaceAugmentation.class) != null)
.filter(rn -> rn.getAugmentation(VppInterfaceAugmentation.class).getPhysicalInterface() != null)
.collect(Collectors.toList());
for (RendererNode rn : vppNodes) {
java.util.Optional<PhysicalInterface>
optResolvedIface =
rn.getAugmentation(VppInterfaceAugmentation.class)
.getPhysicalInterface()
.stream()
.filter(phIface -> phIface.getAddress() != null)
.filter(phIface -> phIface.getAddress()
.stream()
.anyMatch(ipAddr -> NetUtils.isInRange(extSubnetPrefix, String.valueOf(ipAddr.getValue()))))
.findFirst();
if (optResolvedIface.isPresent()) {
return Optional.of(VppIidFactory.getRendererNodeIid(rn)
.builder()
.augmentation(VppInterfaceAugmentation.class)
.child(PhysicalInterface.class, new PhysicalInterfaceKey(optResolvedIface.get().getKey()))
.build());
}
}
return Optional.absent();
}
static List<ExternalIpAddressPool> resolveDynamicNat(@Nonnull PolicyContext policyCtx,
@Nullable List<MappingEntryBuilder> sNatEntries) {
List<RendererForwardingByTenant> forwardingByTenantList =
policyCtx.getPolicy().getConfiguration().getRendererForwarding().getRendererForwardingByTenant();
Map<Long, Ipv4Prefix> extCache = new HashMap<>();
// loop through forwarding by tenant
for (RendererForwardingByTenant rft : forwardingByTenantList) {
// loop through renderer network domain
for (RendererNetworkDomain domain : rft.getRendererNetworkDomain()) {
final SubnetAugmentRenderer subnetAugmentation = domain.getAugmentation(SubnetAugmentRenderer.class);
final Subnet subnet = subnetAugmentation.getSubnet();
if (subnet != null && !subnet.isIsTenant() && subnet.getAllocationPool() != null) {
// loop through allocation pool
for (AllocationPool pool : subnet.getAllocationPool()) {
final IpPrefix subnetIpPrefix = subnet.getIpPrefix();
if (subnetIpPrefix.getIpv4Prefix() != null) {
final String firstEntry = pool.getFirst();
final String lastEntry = pool.getLast();
extCache.putAll(resolveDynamicNatPrefix(subnetIpPrefix.getIpv4Prefix(), firstEntry, lastEntry,
sNatEntries));
}
}
}
}
}
final List<ExternalIpAddressPool> extIps = new ArrayList<>();
for (Entry<Long, Ipv4Prefix> entry : extCache.entrySet()) {
extIps.add(new ExternalIpAddressPoolBuilder().setPoolId(entry.getKey())
.setExternalIpPool(entry.getValue())
.build());
}
return extIps;
}
@VisibleForTesting
private static Map<Long, Ipv4Prefix> resolveDynamicNatPrefix(@Nonnull final Ipv4Prefix prefix,
@Nonnull final String first,
@Nullable final String last,
@Nullable final List<MappingEntryBuilder> natEntries) {
LOG.trace("Resolving Ipv4Prefix. prefix: {}, first: {}, last: {}", prefix.getValue(), first, last);
final SubnetUtils subnet = new SubnetUtils(prefix.getValue());
final Map<Long, Ipv4Prefix> extCache = new HashMap<>();
int min = subnet.getInfo().asInteger(first);
// loop through all addresses
for (String address : subnet.getInfo().getAllAddresses()) {
int asInt = subnet.getInfo().asInteger(address);
if (asInt < min) {
continue;
}
extCache.put(Integer.toUnsignedLong(asInt), new Ipv4Prefix(address + "/32"));
if (last == null || subnet.getInfo().asInteger(address) >= subnet.getInfo().asInteger(last)) {
break;
}
}
if (natEntries != null) {
// remove every static NAT entry from extCache
for (MappingEntryBuilder natEntry : natEntries) {
final Ipv4Address externalSrcAddress = natEntry.getExternalSrcAddress();
final long id = Integer.toUnsignedLong(subnet.getInfo().asInteger(externalSrcAddress.getValue()));
if (extCache.get(id) != null) {
extCache.remove(id);
}
}
}
return extCache;
}
public static void resolveOutboundNatInterface(DataBroker mountpoint, InstanceIdentifier<Node> mountPointIid,
NodeId nodeId, Map<NodeId, PhysicalInterfaceKey> extInterfaces) {
if (extInterfaces.containsKey(nodeId)){
PhysicalInterfaceKey physicalInterfaceKey = extInterfaces.get(nodeId);
Optional<Interfaces> readIfaces = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
InstanceIdentifier.create(Interfaces.class), mountpoint.newReadOnlyTransaction());
if(readIfaces.isPresent() ) {
for (Interface nodeInterface : readIfaces.get().getInterface()) {
if (nodeInterface.getName().equals(physicalInterfaceKey.getInterfaceName())) {
LOG.trace("Setting outbound NAT on interface {} on node: {}", nodeInterface.getName(), mountPointIid);
NatUtil.setOutboundInterface(nodeInterface, mountpoint);
}
}
}
}
}
}