blob: 68b32e577db4579c1085b182b7219bec4457375c [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.network;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd;
import org.apache.cloudstack.api.command.admin.network.DeleteGuestNetworkIpv6PrefixCmd;
import org.apache.cloudstack.api.command.admin.network.ListGuestNetworkIpv6PrefixesCmd;
import org.apache.cloudstack.api.command.user.ipv6.CreateIpv6FirewallRuleCmd;
import org.apache.cloudstack.api.command.user.ipv6.DeleteIpv6FirewallRuleCmd;
import org.apache.cloudstack.api.command.user.ipv6.ListIpv6FirewallRulesCmd;
import org.apache.cloudstack.api.command.user.ipv6.UpdateIpv6FirewallRuleCmd;
import org.apache.cloudstack.api.response.Ipv6RouteResponse;
import org.apache.cloudstack.api.response.VpcResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.api.ApiDBUtils;
import com.cloud.configuration.Resource;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterGuestIpv6Prefix;
import com.cloud.dc.DataCenterGuestIpv6PrefixVO;
import com.cloud.dc.Vlan;
import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.DataCenterGuestIpv6PrefixDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.ActionEventUtils;
import com.cloud.event.EventTypes;
import com.cloud.event.EventVO;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao;
import com.cloud.network.dao.NetworkDetailsDao;
import com.cloud.network.firewall.FirewallService;
import com.cloud.network.guru.PublicNetworkGuru;
import com.cloud.network.router.VirtualRouter;
import com.cloud.network.rules.FirewallManager;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.FirewallRuleVO;
import com.cloud.network.vpc.Vpc;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentLifecycleBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionCallbackWithException;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
import com.cloud.vm.NicVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
import com.googlecode.ipv6.IPv6Address;
import com.googlecode.ipv6.IPv6Network;
import com.googlecode.ipv6.IPv6NetworkMask;
public class Ipv6ServiceImpl extends ComponentLifecycleBase implements Ipv6Service {
public static final Logger s_logger = Logger.getLogger(Ipv6ServiceImpl.class.getName());
private static final String s_publicNetworkReserver = PublicNetworkGuru.class.getSimpleName();
ScheduledExecutorService _ipv6GuestPrefixSubnetNetworkMapStateScanner;
@Inject
NetworkOfferingDao networkOfferingDao;
@Inject
VlanDao vlanDao;
@Inject
DataCenterGuestIpv6PrefixDao dataCenterGuestIpv6PrefixDao;
@Inject
Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao;
@Inject
FirewallRulesDao firewallDao;
@Inject
FirewallService firewallService;
@Inject
NetworkDetailsDao networkDetailsDao;
@Inject
NicDao nicDao;
@Inject
DomainRouterDao domainRouterDao;
@Inject
AccountManager accountManager;
@Inject
NetworkModel networkModel;
@Inject
IPAddressDao ipAddressDao;
@Inject
FirewallManager firewallManager;
@Inject
NetworkOrchestrationService networkOrchestrationService;
private boolean isPublicIpv6PlaceholderNic(NicVO nic) {
return ObjectUtils.allNotNull(nic.getIPv6Address(), nic.getIPv6Cidr(), nic.getIPv6Gateway()) &&
s_publicNetworkReserver.equals(nic.getReserver());
}
private Pair<String, ? extends Vlan> getPublicIpv6FromNetworkPlaceholder(Network network, List<VlanVO> ranges) {
List<NicVO> placeholderNics = nicDao.listPlaceholderNicsByNetworkIdAndVmType(network.getId(), VirtualMachine.Type.DomainRouter);
if (CollectionUtils.isEmpty(placeholderNics)) {
return null;
}
Optional<NicVO> nicOptional = placeholderNics.stream().filter(this::isPublicIpv6PlaceholderNic).findFirst();
if (nicOptional.isEmpty()) {
return null;
}
NicVO nic = nicOptional.get();
Optional<VlanVO> vlanOptional = ranges.stream().filter(v -> nic.getIPv6Cidr().equals(v.getIp6Cidr()) && nic.getIPv6Gateway().equals(v.getIp6Gateway())).findFirst();
if (vlanOptional.isEmpty()) {
s_logger.error(String.format("Public IPv6 placeholder NIC with cidr: %s, gateway: %s for network ID: %d is not present in the allocated VLAN: %s",
nic.getIPv6Cidr(), nic.getIPv6Gateway(),network.getId(), ranges.get(0).getVlanTag()));
return null;
}
return new Pair<>(nic.getIPv6Address(), vlanOptional.get());
}
private void publishPublicIpv6AssignActionEvent(final Network network, final String ipv6Address) {
String event = EventTypes.EVENT_NET_IP6_ASSIGN;
final String description = String.format("Assigned public IPv6 address: %1$s for network ID: %2$s. Subnet: %3$s, gateway: %1$s", ipv6Address, network.getUuid(), network.getIp6Cidr());
ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), network.getAccountId(), EventVO.LEVEL_INFO, event, description, network.getId(), ApiCommandResourceType.Network.toString(), 0);
final boolean usageHidden = networkDetailsDao.isNetworkUsageHidden(network.getId());
final String guestType = Vlan.VlanType.VirtualNetwork.toString();
UsageEventUtils.publishUsageEvent(event, network.getAccountId(), network.getDataCenterId(), 0L,
ipv6Address, false, guestType, false, usageHidden,
IPv6Network.class.getName(), null);
}
private void publishPublicIpv6ReleaseActionEvent(Network network, String nicIpv6Address) {
String event = EventTypes.EVENT_NET_IP6_RELEASE;
String description = String.format("Released public IPv6 address: %1$s from network ID: %2$s. Subnet: %3$s, gateway: %1$s", nicIpv6Address, network.getUuid(), network.getIp6Cidr());
ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), network.getAccountId(), EventVO.LEVEL_INFO, event, description, network.getId(), ApiCommandResourceType.Network.toString(), 0);
final boolean usageHidden = networkDetailsDao.isNetworkUsageHidden(network.getId());
UsageEventUtils.publishUsageEvent(event, network.getAccountId(), network.getDataCenterId(), 0L,
nicIpv6Address, false, Vlan.VlanType.VirtualNetwork.toString(), false, usageHidden,
IPv6Address.class.getName(), null);
}
private void processPublicIpv6AddressUpdateForVpcTier(final Network network, final Nic nic) {
if (network.getVpcId() == null || !Network.State.Implementing.equals(network.getState())) {
return;
}
DomainRouterVO router = domainRouterDao.findById(nic.getInstanceId());
if (router == null) {
return;
}
if (router.getIsRedundantRouter() && !VirtualRouter.RedundantState.PRIMARY.equals(router.getRedundantState())) {
return;
}
networkOrchestrationService.savePlaceholderNic(network, null, nic.getIPv6Address(), nic.getIPv6Cidr(), nic.getIPv6Gateway(), s_publicNetworkReserver, VirtualMachine.Type.DomainRouter);
publishPublicIpv6AssignActionEvent(network, nic.getIPv6Address());
}
private Pair<String, ? extends Vlan> assignPublicIpv6ToNetworkInternal(Network network, String vlanId, String nicMacAddress) throws InsufficientAddressCapacityException {
final List<VlanVO> ranges = vlanDao.listIpv6RangeByZoneIdAndVlanId(network.getDataCenterId(), vlanId);
if (CollectionUtils.isEmpty(ranges)) {
s_logger.error(String.format("Unable to find IPv6 address for zone ID: %d, physical network ID: %d, VLAN: %s", network.getDataCenterId(), network.getPhysicalNetworkId(), vlanId));
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", DataCenter.class, network.getDataCenterId());
ex.addProxyObject(ApiDBUtils.findZoneById(network.getDataCenterId()).getUuid());
throw ex;
}
Pair<String, ? extends Vlan> placeholderResult = getPublicIpv6FromNetworkPlaceholder(network, ranges);
if (placeholderResult != null) {
return placeholderResult;
}
Pair<String, ? extends Vlan> result = Transaction.execute((TransactionCallbackWithException<Pair<String, ? extends Vlan>, InsufficientAddressCapacityException>) status -> {
removePublicIpv6PlaceholderNics(network);
Collections.shuffle(ranges);
VlanVO selectedVlan = ranges.get(0);
IPv6Network ipv6Network = IPv6Network.fromString(selectedVlan.getIp6Cidr());
if (ipv6Network.getNetmask().asPrefixLength() < IPV6_SLAAC_CIDR_NETMASK) {
Iterator<IPv6Network> splits = ipv6Network.split(IPv6NetworkMask.fromPrefixLength(IPV6_SLAAC_CIDR_NETMASK));
if (splits.hasNext()) {
ipv6Network = splits.next();
}
}
IPv6Address ipv6Addr = NetUtils.EUI64Address(ipv6Network, nicMacAddress);
networkOrchestrationService.savePlaceholderNic(network, null, ipv6Addr.toString(), selectedVlan.getIp6Cidr(), selectedVlan.getIp6Gateway(), s_publicNetworkReserver, VirtualMachine.Type.DomainRouter);
return new Pair<>(ipv6Addr.toString(), selectedVlan);
});
final String ipv6Address = result.first();
publishPublicIpv6AssignActionEvent(network, ipv6Address);
return result;
}
private Ipv6GuestPrefixSubnetNetworkMapVO preallocatePrefixSubnetRandomly(DataCenterGuestIpv6PrefixVO prefix) {
Ipv6GuestPrefixSubnetNetworkMapVO ip6Subnet = null;
List<Ipv6GuestPrefixSubnetNetworkMapVO> prefixSubnetNetworkMapVOList = ipv6GuestPrefixSubnetNetworkMapDao.listUsedByPrefix(prefix.getId());
List<String> usedPrefixList = prefixSubnetNetworkMapVOList.stream().map(Ipv6GuestPrefixSubnetNetworkMap::getSubnet).collect(Collectors.toList());
final IPv6Network ip6Prefix = IPv6Network.fromString(prefix.getPrefix());
Iterator<IPv6Network> splits = ip6Prefix.split(IPv6NetworkMask.fromPrefixLength(IPV6_SLAAC_CIDR_NETMASK));
List<IPv6Network> availableSubnets = new ArrayList<>();
while (splits.hasNext()) {
IPv6Network i = splits.next();
if (!usedPrefixList.contains(i.toString())) {
availableSubnets.add(i);
}
}
if (CollectionUtils.isNotEmpty(availableSubnets)) {
Random r = new Random();
IPv6Network subnet = availableSubnets.get(r.nextInt(availableSubnets.size()));
ip6Subnet = new Ipv6GuestPrefixSubnetNetworkMapVO(prefix.getId(), subnet.toString(), null, Ipv6GuestPrefixSubnetNetworkMap.State.Allocating);
}
return ip6Subnet;
}
protected void releaseIpv6Subnet(long subnetId) {
Ipv6GuestPrefixSubnetNetworkMapVO ipv6GuestPrefixSubnetNetworkMapVO = ipv6GuestPrefixSubnetNetworkMapDao.createForUpdate(subnetId);
ipv6GuestPrefixSubnetNetworkMapVO.setState(Ipv6GuestPrefixSubnetNetworkMap.State.Free);
ipv6GuestPrefixSubnetNetworkMapVO.setNetworkId(null);
ipv6GuestPrefixSubnetNetworkMapVO.setUpdated(new Date());
ipv6GuestPrefixSubnetNetworkMapDao.update(ipv6GuestPrefixSubnetNetworkMapVO.getId(), ipv6GuestPrefixSubnetNetworkMapVO);
}
@Override
public boolean start() {
_ipv6GuestPrefixSubnetNetworkMapStateScanner.scheduleWithFixedDelay(new Ipv6GuestPrefixSubnetNetworkMapStateScanner(), 300, 30*60, TimeUnit.SECONDS);
return true;
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_name = name;
_configParams = params;
_ipv6GuestPrefixSubnetNetworkMapStateScanner = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Ipv6GuestPrefixSubnet-State-Scanner"));
return true;
}
@Override
public List<Class<?>> getCommands() {
final List<Class<?>> cmdList = new ArrayList<Class<?>>();
cmdList.add(CreateGuestNetworkIpv6PrefixCmd.class);
cmdList.add(ListGuestNetworkIpv6PrefixesCmd.class);
cmdList.add(DeleteGuestNetworkIpv6PrefixCmd.class);
cmdList.add(CreateIpv6FirewallRuleCmd.class);
cmdList.add(ListIpv6FirewallRulesCmd.class);
cmdList.add(UpdateIpv6FirewallRuleCmd.class);
cmdList.add(DeleteIpv6FirewallRuleCmd.class);
return cmdList;
}
@Override
public String getConfigComponentName() {
return Ipv6Service.class.getSimpleName();
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {
Ipv6OfferingCreationEnabled,
Ipv6PrefixSubnetCleanupInterval
};
}
@Override
public Pair<Integer, Integer> getUsedTotalIpv6SubnetForPrefix(DataCenterGuestIpv6Prefix prefix) {
List<Ipv6GuestPrefixSubnetNetworkMapVO> usedSubnets = ipv6GuestPrefixSubnetNetworkMapDao.listUsedByPrefix(prefix.getId());
final IPv6Network ip6Prefix = IPv6Network.fromString(prefix.getPrefix());
Iterator<IPv6Network> splits = ip6Prefix.split(IPv6NetworkMask.fromPrefixLength(IPV6_SLAAC_CIDR_NETMASK));
int total = 0;
while(splits.hasNext()) {
total++;
splits.next();
}
return new Pair<>(usedSubnets.size(), total);
}
@Override
public Pair<Integer, Integer> getUsedTotalIpv6SubnetForZone(long zoneId) {
int used = 0;
int total = 0;
List<DataCenterGuestIpv6PrefixVO> prefixes = dataCenterGuestIpv6PrefixDao.listByDataCenterId(zoneId);
for (DataCenterGuestIpv6PrefixVO prefix : prefixes) {
Pair<Integer, Integer> usedTotal = getUsedTotalIpv6SubnetForPrefix(prefix);
used += usedTotal.first();
total += usedTotal.second();
}
return new Pair<>(used, total);
}
public Pair<String, String> preAllocateIpv6SubnetForNetwork(long zoneId) throws ResourceAllocationException {
return Transaction.execute((TransactionCallbackWithException<Pair<String, String>, ResourceAllocationException>) status -> {
List<DataCenterGuestIpv6PrefixVO> prefixes = dataCenterGuestIpv6PrefixDao.listByDataCenterId(zoneId);
if (CollectionUtils.isEmpty(prefixes)) {
s_logger.error(String.format("IPv6 prefixes not found for the zone ID: %d", zoneId));
throw new ResourceAllocationException("Unable to allocate IPv6 network", Resource.ResourceType.network);
}
Ipv6GuestPrefixSubnetNetworkMapVO ip6Subnet = null;
for (DataCenterGuestIpv6PrefixVO prefix : prefixes) {
ip6Subnet = ipv6GuestPrefixSubnetNetworkMapDao.findFirstAvailable(prefix.getId());
if (ip6Subnet == null) {
ip6Subnet = preallocatePrefixSubnetRandomly(prefix);
}
if (ip6Subnet != null) {
break;
}
}
if (ip6Subnet == null) {
throw new ResourceAllocationException("Unable to allocate IPv6 guest subnet for the network", Resource.ResourceType.network);
}
ip6Subnet.setUpdated(new Date());
if (Ipv6GuestPrefixSubnetNetworkMap.State.Free.equals(ip6Subnet.getState())) {
ip6Subnet.setState(Ipv6GuestPrefixSubnetNetworkMap.State.Allocating);
ipv6GuestPrefixSubnetNetworkMapDao.update(ip6Subnet.getId(), ip6Subnet);
} else {
ipv6GuestPrefixSubnetNetworkMapDao.persist(ip6Subnet);
}
IPv6Network network = IPv6Network.fromString(ip6Subnet.getSubnet());
IPv6Address gateway = network.getFirst().add(1);
return new Pair<>(gateway.toString(), network.toString());
});
}
@Override
public void assignIpv6SubnetToNetwork(String subnet, long networkId) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
Ipv6GuestPrefixSubnetNetworkMapVO ipv6GuestPrefixSubnetNetworkMapVO = ipv6GuestPrefixSubnetNetworkMapDao.findBySubnet(subnet);
if (ipv6GuestPrefixSubnetNetworkMapVO != null) {
ipv6GuestPrefixSubnetNetworkMapVO = ipv6GuestPrefixSubnetNetworkMapDao.createForUpdate(ipv6GuestPrefixSubnetNetworkMapVO.getId());
ipv6GuestPrefixSubnetNetworkMapVO.setState(Ipv6GuestPrefixSubnetNetworkMap.State.Allocated);
ipv6GuestPrefixSubnetNetworkMapVO.setNetworkId(networkId);
ipv6GuestPrefixSubnetNetworkMapVO.setUpdated(new Date());
ipv6GuestPrefixSubnetNetworkMapDao.update(ipv6GuestPrefixSubnetNetworkMapVO.getId(), ipv6GuestPrefixSubnetNetworkMapVO);
}
}
});
}
@Override
public void releaseIpv6SubnetForNetwork(long networkId) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
Ipv6GuestPrefixSubnetNetworkMapVO ipv6GuestPrefixSubnetNetworkMapVO = ipv6GuestPrefixSubnetNetworkMapDao.findByNetworkId(networkId);
if (ipv6GuestPrefixSubnetNetworkMapVO != null) {
releaseIpv6Subnet(ipv6GuestPrefixSubnetNetworkMapVO.getId());
}
}
});
}
@Override
public List<String> getAllocatedIpv6FromVlanRange(Vlan vlan) {
if (ObjectUtils.allNull(vlan.getIp6Cidr(), vlan.getIp6Gateway())) {
return null;
}
List<NicVO> nics = nicDao.findNicsByIpv6GatewayIpv6CidrAndReserver(vlan.getIp6Gateway(), vlan.getIp6Cidr(), s_publicNetworkReserver);
if (CollectionUtils.isNotEmpty(nics)) {
return nics.stream().map(NicVO::getIPv6Address).collect(Collectors.toList());
}
return null;
}
@Override
public Nic assignPublicIpv6ToNetwork(Network network, Nic nic) {
if (StringUtils.isNotEmpty(nic.getIPv6Address())) {
processPublicIpv6AddressUpdateForVpcTier(network, nic);
return nic;
}
try {
Pair<String, ? extends Vlan> publicIpv6AddressVlanPair = assignPublicIpv6ToNetworkInternal(network, nic.getBroadcastUri().toString(), nic.getMacAddress());
Vlan vlan = publicIpv6AddressVlanPair.second();
final String ipv6Address = publicIpv6AddressVlanPair.first();
final String ipv6Gateway = vlan.getIp6Gateway();
final String ipv6Cidr = vlan.getIp6Cidr();
NicVO nicVO = nicDao.createForUpdate(nic.getId());
nicVO.setIPv6Address(ipv6Address);
nicVO.setIPv6Gateway(ipv6Gateway);
nicVO.setIPv6Cidr(ipv6Cidr);
nicDao.update(nic.getId(), nicVO);
return nicVO;
} catch (InsufficientAddressCapacityException ex) {
throw new CloudRuntimeException(ex.getMessage());
}
}
@Override
public void updateNicIpv6(NicProfile nic, DataCenter dc, Network network) throws InsufficientAddressCapacityException {
boolean isIpv6Supported = networkOfferingDao.isIpv6Supported(network.getNetworkOfferingId());
if (nic.getIPv6Address() == null && isIpv6Supported) {
Pair<String, ? extends Vlan> publicIpv6AddressVlanPair = assignPublicIpv6ToNetworkInternal(network, nic.getBroadCastUri().toString(), nic.getMacAddress());
final Vlan vlan = publicIpv6AddressVlanPair.second();
final String routerIpv6 = publicIpv6AddressVlanPair.first();
final String routerIpv6Gateway = vlan.getIp6Gateway();
final String routerIpv6Cidr = vlan.getIp6Cidr();
nic.setIPv6Address(routerIpv6);
nic.setIPv6Gateway(routerIpv6Gateway);
nic.setIPv6Cidr(routerIpv6Cidr);
if (nic.getIPv4Address() != null) {
nic.setFormat(Networks.AddressFormat.DualStack);
} else {
nic.setFormat(Networks.AddressFormat.Ip6);
}
Pair<String, String> dns = networkModel.getNetworkIp6Dns(network, dc);
nic.setIPv6Dns1(dns.first());
nic.setIPv6Dns2(dns.second());
}
}
@Override
public List<String> getPublicIpv6AddressesForNetwork(Network network) {
List<String> addresses = new ArrayList<>();
List<DomainRouterVO> routers = domainRouterDao.findByNetwork(network.getId());
for (DomainRouterVO router : routers) {
List<NicVO> nics = nicDao.listByVmId(router.getId());
for (NicVO nic : nics) {
String address = nic.getIPv6Address();
if (!s_publicNetworkReserver.equals(nic.getReserver()) ||
StringUtils.isEmpty(address) ||
addresses.contains(address)) {
continue;
}
addresses.add(address);
}
}
return addresses;
}
@Override
public void updateIpv6RoutesForVpcResponse(Vpc vpc, VpcResponse response) {
Set<Ipv6RouteResponse> ipv6Routes = new LinkedHashSet<>();
List<? extends Network> networks = networkModel.listNetworksByVpc(vpc.getId());
for (Network network : networks) {
if (networkOfferingDao.isIpv6Supported(network.getNetworkOfferingId())) {
List<String> networkPublicIpv6 = getPublicIpv6AddressesForNetwork(network);
for (String address : networkPublicIpv6) {
Ipv6RouteResponse route = new Ipv6RouteResponse(network.getIp6Cidr(), address);
ipv6Routes.add(route);
}
}
}
if (CollectionUtils.isNotEmpty(ipv6Routes)) {
response.setIpv6Routes(ipv6Routes);
}
}
@Override
public void checkNetworkIpv6Upgrade(Network network) throws InsufficientAddressCapacityException, ResourceAllocationException {
List<DataCenterGuestIpv6PrefixVO> prefixes = dataCenterGuestIpv6PrefixDao.listByDataCenterId(network.getDataCenterId());
if (CollectionUtils.isEmpty(prefixes)) {
s_logger.error(String.format("IPv6 prefixes not found for the zone ID: %d", network.getDataCenterId()));
throw new ResourceAllocationException("Unable to allocate IPv6 network", Resource.ResourceType.network);
}
List<IPAddressVO> addresses = network.getVpcId() == null ?
ipAddressDao.listByAssociatedNetwork(network.getId(), true) :
ipAddressDao.listByAssociatedVpc(network.getVpcId(), true);
for (IPAddressVO address : addresses) {
VlanVO vlan = vlanDao.findById(address.getVlanId());
final List<VlanVO> ranges = vlanDao.listIpv6RangeByZoneIdAndVlanId(network.getDataCenterId(), vlan.getVlanTag());
if (CollectionUtils.isEmpty(ranges)) {
s_logger.error(String.format("Unable to find IPv6 address for zone ID: %d, physical network ID: %d, VLAN: %s", network.getDataCenterId(), network.getPhysicalNetworkId(), vlan.getVlanTag()));
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", DataCenter.class, network.getDataCenterId());
ex.addProxyObject(ApiDBUtils.findZoneById(network.getDataCenterId()).getUuid());
throw ex;
}
}
}
@Override
public Pair<List<? extends FirewallRule>, Integer> listIpv6FirewallRules(ListIpv6FirewallRulesCmd listIpv6FirewallRulesCmd) {
return firewallService.listFirewallRules(listIpv6FirewallRulesCmd);
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_FIREWALL_OPEN, eventDescription = "creating IPv6 firewall rule", create = true)
public FirewallRule createIpv6FirewallRule(CreateIpv6FirewallRuleCmd cmd) throws NetworkRuleConflictException {
final Account caller = CallContext.current().getCallingAccount();
final long networkId = cmd.getNetworkId();
final Integer portStart = cmd.getSourcePortStart();
final Integer portEnd = cmd.getSourcePortEnd();
final FirewallRule.TrafficType trafficType = cmd.getTrafficType();
final String protocol = cmd.getProtocol();
final Integer icmpCode = cmd.getIcmpCode();
final Integer icmpType = cmd.getIcmpType();
final boolean forDisplay = cmd.isDisplay();
final FirewallRule.FirewallRuleType type = FirewallRule.FirewallRuleType.User;
final List<String> sourceCidrList = cmd.getSourceCidrList();
final List<String> destinationCidrList = cmd.getDestinationCidrList();
for (String cidr : sourceCidrList) {
if (!NetUtils.isValidIp6Cidr(cidr)) {
throw new InvalidParameterValueException(String.format("Invalid source IPv6 CIDR: %s", cidr));
}
}
for (String cidr : destinationCidrList) {
if (!NetUtils.isValidIp6Cidr(cidr)) {
throw new InvalidParameterValueException(String.format("Invalid destination IPv6 CIDR: %s", cidr));
}
}
if (portStart != null && !NetUtils.isValidPort(portStart)) {
throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart);
}
if (portEnd != null && !NetUtils.isValidPort(portEnd)) {
throw new InvalidParameterValueException("Public port range is an invalid value: " + portEnd);
}
if (ObjectUtils.allNotNull(portStart, portEnd) && portStart > portEnd) {
throw new InvalidParameterValueException("Start port can't be bigger than end port");
}
Network network = networkModel.getNetwork(networkId);
assert network != null : "Can't create rule as network is null?";
final long accountId = network.getAccountId();
final long domainId = network.getDomainId();
if (FirewallRule.TrafficType.Egress.equals(trafficType)) {
accountManager.checkAccess(caller, null, true, network);
}
// Verify that the network guru supports the protocol specified
Map<Network.Capability, String> caps = networkModel.getNetworkServiceCapabilities(network.getId(), Network.Service.Firewall);
if (caps != null) {
String supportedProtocols;
String supportedTrafficTypes = null;
supportedTrafficTypes = caps.get(Network.Capability.SupportedTrafficDirection).toLowerCase();
if (trafficType == FirewallRule.TrafficType.Egress) {
supportedProtocols = caps.get(Network.Capability.SupportedEgressProtocols).toLowerCase();
} else {
supportedProtocols = caps.get(Network.Capability.SupportedProtocols).toLowerCase();
}
if (!supportedProtocols.contains(protocol.toLowerCase())) {
throw new InvalidParameterValueException(String.format("Protocol %s is not supported in zone", protocol));
} else if (!supportedTrafficTypes.contains(trafficType.toString().toLowerCase())) {
throw new InvalidParameterValueException("Traffic Type " + trafficType + " is currently supported by Firewall in network " + networkId);
}
}
// icmp code and icmp type can't be passed in for any other protocol rather than icmp
if (!protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (icmpCode != null || icmpType != null)) {
throw new InvalidParameterValueException("Can specify icmpCode and icmpType for ICMP protocol only");
}
if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (portStart != null || portEnd != null)) {
throw new InvalidParameterValueException("Can't specify start/end port when protocol is ICMP");
}
return Transaction.execute(new TransactionCallbackWithException<FirewallRuleVO, NetworkRuleConflictException>() {
@Override
public FirewallRuleVO doInTransaction(TransactionStatus status) throws NetworkRuleConflictException {
FirewallRuleVO newRule =
new FirewallRuleVO(null, null, portStart, portEnd, protocol.toLowerCase(), networkId, accountId, domainId, FirewallRule.Purpose.Ipv6Firewall,
sourceCidrList, destinationCidrList, icmpCode, icmpType, null, trafficType);
newRule.setType(type);
newRule.setDisplay(forDisplay);
newRule = firewallDao.persist(newRule);
if (FirewallRule.FirewallRuleType.User.equals(type)) {
firewallManager.detectRulesConflict(newRule);
}
if (!firewallDao.setStateToAdd(newRule)) {
throw new CloudRuntimeException("Unable to update the state to add for " + newRule);
}
CallContext.current().setEventDetails("Rule Id: " + newRule.getId());
return newRule;
}
});
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_FIREWALL_CLOSE, eventDescription = "revoking IPv6 firewall rule", async = true)
public boolean revokeIpv6FirewallRule(Long id) {
FirewallRuleVO rule = firewallDao.findById(id);
if (rule == null) {
throw new InvalidParameterValueException(String.format("Unable to find IPv6 firewall rule with id %d", id));
}
if (FirewallRule.TrafficType.Ingress.equals(rule.getTrafficType())) {
return firewallManager.revokeIngressFirewallRule(rule.getId(), true);
}
return firewallManager.revokeEgressFirewallRule(rule.getId(), true);
}
@ActionEvent(eventType = EventTypes.EVENT_FIREWALL_UPDATE, eventDescription = "updating IPv6 firewall rule", async = true)
public FirewallRule updateIpv6FirewallRule(UpdateIpv6FirewallRuleCmd cmd) {
final long id = cmd.getId();
final boolean forDisplay = cmd.isDisplay();
FirewallRuleVO rule = firewallDao.findById(id);
if (rule == null) {
throw new InvalidParameterValueException(String.format("Unable to find IPv6 firewall rule with id %d", id));
}
if (FirewallRule.TrafficType.Ingress.equals(rule.getTrafficType())) {
return firewallManager.updateIngressFirewallRule(rule.getId(), null, forDisplay);
}
return firewallManager.updateEgressFirewallRule(rule.getId(), null, forDisplay);
}
@Override
public FirewallRule getIpv6FirewallRule(Long entityId) {
return firewallDao.findById(entityId);
}
@Override
public boolean applyIpv6FirewallRule(long id) {
FirewallRuleVO rule = firewallDao.findById(id);
if (rule == null) {
s_logger.error(String.format("Unable to find IPv6 firewall rule with ID: %d", id));
return false;
}
if (!FirewallRule.Purpose.Ipv6Firewall.equals(rule.getPurpose())) {
s_logger.error(String.format("Cannot apply IPv6 firewall rule with ID: %d as purpose %s is not %s", id, rule.getPurpose(), FirewallRule.Purpose.Ipv6Firewall));
}
s_logger.debug(String.format("Applying IPv6 firewall rules for rule with ID: %s", rule.getUuid()));
List<FirewallRuleVO> rules = firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), FirewallRule.TrafficType.Egress);
rules.addAll(firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), FirewallRule.Purpose.Ipv6Firewall, FirewallRule.TrafficType.Ingress));
return firewallManager.applyFirewallRules(rules, false, CallContext.current().getCallingAccount());
}
@Override
public void removePublicIpv6PlaceholderNics(Network network) {
try {
List<NicVO> nics = nicDao.listPlaceholderNicsByNetworkId(network.getId())
.stream().filter(this::isPublicIpv6PlaceholderNic).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(nics)) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
for (Nic nic : nics) {
s_logger.debug("Removing placeholder nic " + nic);
nicDao.remove(nic.getId());
publishPublicIpv6ReleaseActionEvent(network, nic.getIPv6Address());
}
}
});
}
} catch (Exception e) {
String msg = String.format("IPv6 Placeholder Nics trash. Exception: %s", e.getMessage());
s_logger.error(msg);
throw new CloudRuntimeException(msg, e);
}
}
public class Ipv6GuestPrefixSubnetNetworkMapStateScanner extends ManagedContextRunnable {
@Override
protected void runInContext() {
GlobalLock gcLock = GlobalLock.getInternLock("Ipv6GuestPrefixSubnetNetworkMap.State.Scanner.Lock");
try {
if (gcLock.lock(3)) {
try {
reallyRun();
} finally {
gcLock.unlock();
}
}
} finally {
gcLock.releaseRef();
}
}
public void reallyRun() {
try {
List<Ipv6GuestPrefixSubnetNetworkMapVO> subnets = ipv6GuestPrefixSubnetNetworkMapDao.findPrefixesInStates(Ipv6GuestPrefixSubnetNetworkMap.State.Allocating);
for (Ipv6GuestPrefixSubnetNetworkMapVO subnet : subnets) {
if (s_logger.isInfoEnabled()) {
s_logger.info(String.format("Running state scanned on Ipv6GuestPrefixSubnetNetworkMap : %s", subnet.getSubnet()));
}
try {
if ((new Date()).getTime() - subnet.getUpdated().getTime() < Ipv6PrefixSubnetCleanupInterval.value()*1000) {
continue;
}
releaseIpv6Subnet(subnet.getId());
} catch (CloudRuntimeException e) {
s_logger.warn(String.format("Failed to release IPv6 guest prefix subnet : %s during state scan", subnet.getSubnet()), e);
}
}
} catch (Exception e) {
s_logger.warn("Caught exception while running Ipv6GuestPrefixSubnetNetworkMap state scanner: ", e);
}
}
}
}