package io.fathom.cloud.compute.networks; import io.fathom.cloud.CloudException; import io.fathom.cloud.server.model.Project; import java.net.InetAddress; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.net.InetAddresses; public abstract class NetworkPoolBase implements NetworkPool { private static final Logger log = LoggerFactory.getLogger(NetworkPoolBase.class); public abstract String getNetworkKey(); protected abstract NetworkPoolAllocation markIpAllocated0(Project project, InetAddress ip) throws CloudException; protected abstract InetAddress getGateway(); protected abstract List<InetAddress> getAllocatedIps() throws CloudException; protected Set<String> getAddressesInUse() throws CloudException { Set<String> reserved = Sets.newHashSet(); for (InetAddress ip : getSystemReservedAddresses()) { reserved.add(InetAddresses.toAddrString(ip)); } for (InetAddress ip : getAllocatedIps()) { reserved.add(InetAddresses.toAddrString(ip)); } return reserved; } protected List<InetAddress> getSystemReservedAddresses() { IpRange ipRange = getIpRange(); // TODO: Anything else reserved? Broadcast address? Configurable? byte[] mask = ipRange.getNetmaskBytes(); byte[] prefix = ipRange.getAddress().getAddress(); List<InetAddress> reserved = Lists.newArrayList(); { // Reserve the router/host address reserved.add(ipRange.getAddress()); } { // Reserve the gateway InetAddress gateway = getGateway(); if (gateway != null) { reserved.add(gateway); } } { // Reserve the .0 address byte[] ip = new byte[prefix.length]; System.arraycopy(prefix, 0, ip, 0, ip.length); for (int i = 0; i < mask.length; i++) { ip[i] &= mask[i]; } reserved.add(NetworkPools.toAddress(ip)); } { // Reserve the broadcast address byte[] ip = new byte[prefix.length]; System.arraycopy(prefix, 0, ip, 0, ip.length); for (int i = 0; i < mask.length; i++) { ip[i] &= mask[i]; } for (int i = 0; i < mask.length; i++) { ip[i] |= ~mask[i]; } InetAddress addr = NetworkPools.toAddress(ip); reserved.add(addr); log.debug("Reserved broadcast address in {}: {}", ipRange, addr); } return reserved; } protected abstract IpRange getIpRange(); @Override public InetAddress checkIpAvailable(byte[] seed) throws CloudException { InetAddress address = convertSeedToIp(seed); Set<String> reserved = getAddressesInUse(); if (reserved.contains(InetAddresses.toAddrString(address))) { return null; } return address; } protected InetAddress convertSeedToIp(byte[] seed) { IpRange ipRange = getIpRange(); byte[] mask = ipRange.getNetmaskBytes(); byte[] prefix = ipRange.getAddress().getAddress(); byte[] ip = new byte[mask.length]; System.arraycopy(seed, seed.length - ip.length, ip, 0, ip.length); // Apply netmask for (int i = 0; i < mask.length; i++) { ip[i] &= ~mask[i]; ip[i] |= (prefix[i] & mask[i]); } // if (ip.length == 16) { // // We always allocate a /112 for IPv6 // ip[14] = 0; // ip[15] = 0; // } InetAddress address = NetworkPools.toAddress(ip); return address; } @Override public NetworkPoolAllocation markIpAllocated(Project project, InetAddress ip) throws CloudException { Set<String> reserved = getAddressesInUse(); if (reserved.contains(InetAddresses.toAddrString(ip))) { return null; } NetworkPoolAllocation created = markIpAllocated0(project, ip); return created; } }