diff --git a/api/src/com/cloud/agent/api/to/NetworkTO.java b/api/src/com/cloud/agent/api/to/NetworkTO.java index be11deaa1ec..9c888059d93 100644 --- a/api/src/com/cloud/agent/api/to/NetworkTO.java +++ b/api/src/com/cloud/agent/api/to/NetworkTO.java @@ -38,6 +38,8 @@ public class NetworkTO { protected URI isolationUri; protected boolean isSecurityGroupEnabled; protected String name; + protected String ip6address; + protected String ip6cidr; public NetworkTO() { } @@ -62,6 +64,14 @@ public class NetworkTO { this.ip = ip; } + public void setIp6Address(String addr) { + this.ip6address = addr; + } + + public void setIp6Cidr(String cidr) { + this.ip6cidr = cidr; + } + public void setNetmask(String netmask) { this.netmask = netmask; } @@ -114,6 +124,7 @@ public class NetworkTO { * the full information about what is needed. * * @param ip + * @param ip6address * @param vlan * @param netmask * @param mac @@ -130,10 +141,30 @@ public class NetworkTO { this.dns2 = dns2; } + public NetworkTO(String ip, String netmask, String mac, String gateway, String dns1, String dns2, String ip6address, + String ip6cidr) { + this.ip = ip; + this.netmask = netmask; + this.mac = mac; + this.gateway = gateway; + this.dns1 = dns1; + this.dns2 = dns2; + this.ip6address = ip6address; + this.ip6cidr = ip6cidr; + } + public String getIp() { return ip; } + public String getIp6Address() { + return ip6address; + } + + public String getIp6Cidr() { + return ip6cidr; + } + public String getNetmask() { return netmask; } diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java index e48c1f587a0..c413eddc1a8 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java @@ -87,6 +87,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis to.setNetworkRateMbps(profile.getNetworkRate()); to.setName(profile.getName()); to.setSecurityGroupEnabled(profile.isSecurityGroupEnabled()); + to.setIp6Address(profile.getIPv6Address()); + to.setIp6Cidr(profile.getIPv6Cidr()); NetworkVO network = _networkDao.findById(profile.getNetworkId()); to.setNetworkUuid(network.getUuid()); diff --git a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java index 9154ece5079..6301ca50e1b 100644 --- a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java +++ b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java @@ -30,6 +30,7 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan; +import com.cloud.dc.VlanVO; import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.PodVlanMapDao; @@ -54,12 +55,14 @@ import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; import com.cloud.vm.Nic; import com.cloud.vm.Nic.ReservationStrategy; import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; +import com.googlecode.ipv6.IPv6Address; public class DirectPodBasedNetworkGuru extends DirectNetworkGuru { private static final Logger s_logger = Logger.getLogger(DirectPodBasedNetworkGuru.class); @@ -166,54 +169,81 @@ public class DirectPodBasedNetworkGuru extends DirectNetworkGuru { protected void getIp(final NicProfile nic, final Pod pod, final VirtualMachineProfile vm, final Network network) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException { final DataCenter dc = _dcDao.findById(pod.getDataCenterId()); - if (nic.getIPv4Address() == null) { Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws InsufficientAddressCapacityException { PublicIp ip = null; List podRefs = _podVlanDao.listPodVlanMapsByPod(pod.getId()); - String podRangeGateway = null; - if (!podRefs.isEmpty()) { - podRangeGateway = _vlanDao.findById(podRefs.get(0).getVlanDbId()).getVlanGateway(); - } - //Get ip address from the placeholder and don't allocate a new one - if (vm.getType() == VirtualMachine.Type.DomainRouter) { - Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId()); - if (placeholderNic != null) { - IPAddressVO userIp = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), placeholderNic.getIPv4Address()); - ip = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId())); - s_logger.debug("Nic got an ip address " + placeholderNic.getIPv4Address() + " stored in placeholder nic for the network " + network + - " and gateway " + podRangeGateway); + VlanVO vlan = _vlanDao.findById(podRefs.get(0).getVlanDbId()); + + if (nic.getIPv4Address() == null) { + String podRangeGateway = null; + if (!podRefs.isEmpty()) { + podRangeGateway = vlan.getVlanGateway(); } - } - - if (ip == null) { - ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), null, false); - } - - nic.setIPv4Address(ip.getAddress().toString()); - nic.setFormat(AddressFormat.Ip4); - nic.setIPv4Gateway(ip.getGateway()); - nic.setIPv4Netmask(ip.getNetmask()); - if (ip.getVlanTag() != null && ip.getVlanTag().equalsIgnoreCase(Vlan.UNTAGGED)) { - nic.setIsolationUri(IsolationType.Ec2.toUri(Vlan.UNTAGGED)); - nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED)); - nic.setBroadcastType(BroadcastDomainType.Native); - } - nic.setReservationId(String.valueOf(ip.getVlanTag())); - nic.setMacAddress(ip.getMacAddress()); - - //save the placeholder nic if the vm is the Virtual router - if (vm.getType() == VirtualMachine.Type.DomainRouter) { - Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId()); - if (placeholderNic == null) { - s_logger.debug("Saving placeholder nic with ip4 address " + nic.getIPv4Address() + " for the network " + network); - _networkMgr.savePlaceholderNic(network, nic.getIPv4Address(), null, VirtualMachine.Type.DomainRouter); + //Get ip address from the placeholder and don't allocate a new one + if (vm.getType() == VirtualMachine.Type.DomainRouter) { + Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId()); + if (placeholderNic != null) { + IPAddressVO userIp = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), placeholderNic.getIPv4Address()); + ip = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId())); + s_logger.debug("Nic got an ip address " + placeholderNic.getIPv4Address() + " stored in placeholder nic for the network " + network + + " and gateway " + podRangeGateway); + } + } + + if (ip == null) { + ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), null, false); + } + + nic.setIPv4Address(ip.getAddress().toString()); + nic.setFormat(AddressFormat.Ip4); + nic.setIPv4Gateway(ip.getGateway()); + nic.setIPv4Netmask(ip.getNetmask()); + if (ip.getVlanTag() != null && ip.getVlanTag().equalsIgnoreCase(Vlan.UNTAGGED)) { + nic.setIsolationUri(IsolationType.Ec2.toUri(Vlan.UNTAGGED)); + nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED)); + nic.setBroadcastType(BroadcastDomainType.Native); + } + nic.setReservationId(String.valueOf(ip.getVlanTag())); + nic.setMacAddress(ip.getMacAddress()); + + //save the placeholder nic if the vm is the Virtual router + if (vm.getType() == VirtualMachine.Type.DomainRouter) { + Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, pod.getId()); + if (placeholderNic == null) { + s_logger.debug("Saving placeholder nic with ip4 address " + nic.getIPv4Address() + " for the network " + network); + _networkMgr.savePlaceholderNic(network, nic.getIPv4Address(), null, VirtualMachine.Type.DomainRouter); + } } - } } - }); - } + + /** + * 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. + * + * Using Router Advertisements the routers in the network should announce the IPv6 CIDR which is configured + * in in the vlan table in the database. + * + * This way the NIC will be populated with a IPv6 address on which the Instance is reachable. + */ + if (vlan.getIp6Cidr() != null) { + if (nic.getIPv6Address() == null) { + s_logger.debug("Found IPv6 CIDR " + vlan.getIp6Cidr() + " for VLAN " + vlan.getId()); + nic.setIPv6Cidr(vlan.getIp6Cidr()); + nic.setIPv6Gateway(vlan.getIp6Gateway()); + + IPv6Address ipv6addr = NetUtils.EUI64Address(vlan.getIp6Cidr(), nic.getMacAddress()); + s_logger.info("Calculated IPv6 address " + ipv6addr + " using EUI-64 for NIC " + nic.getUuid()); + nic.setIPv6Address(ipv6addr.toString()); + } + } else { + s_logger.debug("No IPv6 CIDR configured for VLAN " + vlan.getId()); + } + } + }); + nic.setIPv4Dns1(dc.getDns1()); nic.setIPv4Dns2(dc.getDns2()); } diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java index a73813c28d8..a2ec4116345 100644 --- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java +++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java @@ -1578,5 +1578,24 @@ public class NetUtils { return !isInRange; } + public static IPv6Address EUI64Address(final IPv6Network cidr, final String macAddress) { + if (cidr.getNetmask().asPrefixLength() > 64) { + throw new IllegalArgumentException("IPv6 subnet " + cidr.toString() + " is not 64 bits or larger in size"); + } + + String mac[] = macAddress.toLowerCase().split(":"); + + return IPv6Address.fromString(cidr.getFirst().toString() + + Integer.toHexString(Integer.parseInt(mac[0], 16) ^ 2) + + mac[1] + ":" + mac[2] + "ff:fe" + mac[3] +":" + mac[4] + mac[5]); + } + + public static IPv6Address EUI64Address(final String cidr, final String macAddress) { + return EUI64Address(IPv6Network.fromString(cidr), macAddress); + } + + public static IPv6Address ipv6LinkLocal(final String macAddress) { + return EUI64Address(IPv6Network.fromString("fe80::/64"), macAddress); + } } diff --git a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java index 490d0df4eb0..4c43751e4c4 100644 --- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java +++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java @@ -526,4 +526,25 @@ public class NetUtilsTest { assertFalse(NetUtils.isNetworkorBroadcastIP("192.168.0.63","255.255.255.128")); } + @Test + public void testIPv6EUI64Address() { + assertEquals(IPv6Address.fromString("2001:db8:100::47a:88ff:fe00:8b"), + NetUtils.EUI64Address("2001:db8:100::/64", "06:7a:88:00:00:8b")); + + assertEquals(IPv6Address.fromString("2a00:f10:121:b00:434:a0ff:fe00:1bc7"), + NetUtils.EUI64Address("2a00:f10:121:b00::/64", "06:34:a0:00:1b:c7")); + + assertEquals(IPv6Address.fromString("2001:980:7936:0:ea2a:eaff:fe58:eb98"), + NetUtils.EUI64Address("2001:980:7936::/64", "e8:2a:ea:58:eb:98")); + + assertEquals(IPv6Address.fromString("2001:980:7936:0:c23f:d5ff:fe68:2808"), + NetUtils.EUI64Address("2001:980:7936::/64", "c0:3f:d5:68:28:08")); + } + + @Test + public void testIPv6LinkLocal() { + assertEquals(IPv6Address.fromString("fe80::fc54:ff:fe00:3e05"), NetUtils.ipv6LinkLocal("fe:54:00:00:3e:05")); + assertEquals(IPv6Address.fromString("fe80::42:e0ff:fee8:d6a3"), NetUtils.ipv6LinkLocal("02:42:e0:e8:d6:a3")); + assertEquals(IPv6Address.fromString("fe80::47a:88ff:fe00:8b"), NetUtils.ipv6LinkLocal("06:7a:88:00:00:8b")); + } }