mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
* netutils: Add method to verify if IPv6 Address is EUI-64 By checking if ff:fe is present in the address we can see if an IPv6 Address is EUI-64 or not. Signed-off-by: Wido den Hollander <wido@widodh.nl> * ipv6: Do not allow a Secondary IPv6 address to be EUI-64 EUI-64 addresses should not be allowed as they can be used in the future by a to be deployed Instance which has to obtain this address because it matches it's MAC. In a /64 subnet there are more then enough other IPs available to be allocated to Instances, therefor we can safely disallow the allocation of EUI-64 addresses. Signed-off-by: Wido den Hollander <wido@widodh.nl>
221 lines
9.5 KiB
Java
221 lines
9.5 KiB
Java
// 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.Map;
|
|
|
|
import javax.inject.Inject;
|
|
import javax.naming.ConfigurationException;
|
|
|
|
import com.cloud.vm.NicProfile;
|
|
import com.googlecode.ipv6.IPv6Address;
|
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
|
import org.apache.log4j.Logger;
|
|
|
|
import com.cloud.configuration.Config;
|
|
import com.cloud.dc.DataCenter;
|
|
import com.cloud.dc.dao.DataCenterDao;
|
|
import com.cloud.dc.dao.VlanDao;
|
|
import com.cloud.exception.InsufficientAddressCapacityException;
|
|
import com.cloud.exception.InvalidParameterValueException;
|
|
import com.cloud.network.IpAddress.State;
|
|
import com.cloud.network.Network.IpAddresses;
|
|
import com.cloud.network.dao.IPAddressDao;
|
|
import com.cloud.network.dao.IPAddressVO;
|
|
import com.cloud.network.dao.UserIpv6AddressDao;
|
|
import com.cloud.user.Account;
|
|
import com.cloud.utils.NumbersUtil;
|
|
import com.cloud.utils.component.ManagerBase;
|
|
import com.cloud.utils.db.DB;
|
|
import com.cloud.utils.net.NetUtils;
|
|
import com.cloud.vm.dao.NicSecondaryIpDao;
|
|
import com.cloud.vm.dao.NicSecondaryIpVO;
|
|
|
|
public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressManager {
|
|
public static final Logger s_logger = Logger.getLogger(Ipv6AddressManagerImpl.class.getName());
|
|
|
|
String _name = null;
|
|
int _ipv6RetryMax = 0;
|
|
|
|
@Inject
|
|
DataCenterDao _dcDao;
|
|
@Inject
|
|
VlanDao _vlanDao;
|
|
@Inject
|
|
NetworkModel _networkModel;
|
|
@Inject
|
|
UserIpv6AddressDao _ipv6Dao;
|
|
@Inject
|
|
ConfigurationDao _configDao;
|
|
@Inject
|
|
IpAddressManager ipAddressManager;
|
|
@Inject
|
|
NicSecondaryIpDao nicSecondaryIpDao;
|
|
@Inject
|
|
IPAddressDao ipAddressDao;
|
|
|
|
@Override
|
|
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
|
_name = name;
|
|
Map<String, String> configs = _configDao.getConfiguration(params);
|
|
_ipv6RetryMax = NumbersUtil.parseInt(configs.get(Config.NetworkIPv6SearchRetryMax.key()), 10000);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Executes method {@link #acquireGuestIpv6Address(Network, String)} and returns the requested IPv6 (String) in case of successfully allocating the guest IPv6 address.
|
|
*/
|
|
@Override
|
|
public String allocateGuestIpv6(Network network, String requestedIpv6) throws InsufficientAddressCapacityException {
|
|
return acquireGuestIpv6Address(network, requestedIpv6);
|
|
}
|
|
|
|
/**
|
|
* Allocates a guest IPv6 address for the guest NIC. It will throw exceptions in the following cases:
|
|
* <ul>
|
|
* <li>there is no IPv6 address available in the network;</li>
|
|
* <li>IPv6 address is equals to the Gateway;</li>
|
|
* <li>the network offering is empty;</li>
|
|
* <li>the IPv6 address is not in the network;</li>
|
|
* <li>the requested IPv6 address is already in use in the network.</li>
|
|
* </ul>
|
|
*/
|
|
@Override
|
|
@DB
|
|
public String acquireGuestIpv6Address(Network network, String requestedIpv6) throws InsufficientAddressCapacityException {
|
|
if (!_networkModel.areThereIPv6AddressAvailableInNetwork(network.getId())) {
|
|
throw new InsufficientAddressCapacityException(
|
|
String.format("There is no IPv6 address available in the network [name=%s, network id=%s]", network.getName(), network.getId()), DataCenter.class,
|
|
network.getDataCenterId());
|
|
}
|
|
|
|
if (NetUtils.isIPv6EUI64(requestedIpv6)) {
|
|
throw new InsufficientAddressCapacityException(String.format("Requested IPv6 address [%s] may not be a EUI-64 address", requestedIpv6), DataCenter.class,
|
|
network.getDataCenterId());
|
|
}
|
|
|
|
checkIfCanAllocateIpv6Address(network, requestedIpv6);
|
|
|
|
IpAddresses requestedIpPair = new IpAddresses(null, requestedIpv6);
|
|
_networkModel.checkRequestedIpAddresses(network.getId(), requestedIpPair);
|
|
|
|
IPAddressVO ip = ipAddressDao.findByIpAndSourceNetworkId(network.getId(), requestedIpv6);
|
|
if (ip != null) {
|
|
State ipState = ip.getState();
|
|
if (ipState != State.Free) {
|
|
throw new InsufficientAddressCapacityException(String.format("Requested ip address [%s] is not free [ip state=%]", requestedIpv6, ipState), DataCenter.class,
|
|
network.getDataCenterId());
|
|
}
|
|
}
|
|
return requestedIpv6;
|
|
}
|
|
|
|
/**
|
|
* Allocates a public IPv6 address for the guest NIC. It will throw exceptions in the following cases:
|
|
* <ul>
|
|
* <li>the the requested IPv6 address is already in use in the network;</li>
|
|
* <li>IPv6 address is equals to the Gateway;</li>
|
|
* <li>the network offering is empty;</li>
|
|
* <li>the IPv6 address is not in the network.</li>
|
|
* </ul>
|
|
*/
|
|
@Override
|
|
public String allocatePublicIp6ForGuestNic(Network network, Long podId, Account owner, String requestedIpv6) throws InsufficientAddressCapacityException {
|
|
checkIfCanAllocateIpv6Address(network, requestedIpv6);
|
|
|
|
return requestedIpv6;
|
|
}
|
|
|
|
/**
|
|
* Performs some checks on the given IPv6 address. It will throw exceptions in the following cases:
|
|
* <ul>
|
|
* <li>the the requested IPv6 address is already in use in the network;</li>
|
|
* <li>IPv6 address is equals to the Gateway;</li>
|
|
* <li>the network offering is empty;</li>
|
|
* <li>the IPv6 address is not in the network.</li>
|
|
* </ul>
|
|
*/
|
|
protected void checkIfCanAllocateIpv6Address(Network network, String ipv6) throws InsufficientAddressCapacityException {
|
|
if (isIp6Taken(network, ipv6)) {
|
|
throw new InsufficientAddressCapacityException(
|
|
String.format("The IPv6 address [%s] is already in use in the network [id=%s, name=%s]", ipv6, network.getId(), network.getName()), Network.class,
|
|
network.getId());
|
|
}
|
|
|
|
if (ipAddressManager.isIpEqualsGatewayOrNetworkOfferingsEmpty(network, ipv6)) {
|
|
throw new InvalidParameterValueException(
|
|
String.format("The network [id=%s] offering is empty or the requested IP address [%s] is equals to the Gateway", network.getId(), ipv6));
|
|
}
|
|
|
|
String networkIp6Cidr = network.getIp6Cidr();
|
|
if (!NetUtils.isIp6InNetwork(ipv6, networkIp6Cidr)) {
|
|
throw new InvalidParameterValueException(
|
|
String.format("The IPv6 address [%s] is not in the network [id=%s, name=%s, ipv6cidr=%s]", ipv6, network.getId(), network.getName(), network.getIp6Cidr()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns false if the requested ipv6 address is taken by some VM, checking on the 'user_ipv6_address' table or 'nic_secondary_ips' table.
|
|
*/
|
|
protected boolean isIp6Taken(Network network, String requestedIpv6) {
|
|
UserIpv6AddressVO ip6Vo = _ipv6Dao.findByNetworkIdAndIp(network.getId(), requestedIpv6);
|
|
NicSecondaryIpVO nicSecondaryIpVO = nicSecondaryIpDao.findByIp6AddressAndNetworkId(requestedIpv6, network.getId());
|
|
return ip6Vo != null || nicSecondaryIpVO != null;
|
|
}
|
|
|
|
/**
|
|
* Calculate the IPv6 Address the Instance will obtain using SLAAC and IPv6 EUI-64
|
|
*
|
|
* Linux, FreeBSD and Windows all calculate the same IPv6 address when configured properly. (SLAAC)
|
|
*
|
|
* Using Router Advertisements the routers in the network should announce the IPv6 CIDR which is configured
|
|
* for the network.
|
|
*
|
|
* It is up to the network administrator to make sure the IPv6 Routers in the network are sending out Router Advertisements
|
|
* with the correct IPv6 (Prefix, DNS, Lifetime) information.
|
|
*
|
|
* This way the NIC will be populated with a IPv6 address on which the Instance is reachable.
|
|
*
|
|
* This method calculates the IPv6 address the Instance will obtain and updates the Nic object with the correct
|
|
* address information.
|
|
*/
|
|
@Override
|
|
public void setNicIp6Address(final NicProfile nic, final DataCenter dc, final Network network) {
|
|
if (network.getIp6Gateway() != null) {
|
|
if (nic.getIPv6Address() == null) {
|
|
s_logger.debug("Found IPv6 CIDR " + network.getIp6Cidr() + " for Network " + network);
|
|
nic.setIPv6Cidr(network.getIp6Cidr());
|
|
nic.setIPv6Gateway(network.getIp6Gateway());
|
|
|
|
IPv6Address ipv6addr = NetUtils.EUI64Address(network.getIp6Cidr(), nic.getMacAddress());
|
|
s_logger.info("Calculated IPv6 address " + ipv6addr + " using EUI-64 for NIC " + nic.getUuid());
|
|
nic.setIPv6Address(ipv6addr.toString());
|
|
|
|
if (nic.getIPv4Address() != null) {
|
|
nic.setFormat(Networks.AddressFormat.DualStack);
|
|
} else {
|
|
nic.setFormat(Networks.AddressFormat.Ip6);
|
|
}
|
|
}
|
|
nic.setIPv6Dns1(dc.getIp6Dns1());
|
|
nic.setIPv6Dns2(dc.getIp6Dns2());
|
|
}
|
|
}
|
|
|
|
}
|