You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ga...@apache.org on 2019/01/16 18:49:13 UTC

[cloudstack] branch master updated: ipv6: Calculate IPv6 address instead of fetching one from a pool (#3077)

This is an automated email from the ASF dual-hosted git repository.

gabriel pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/master by this push:
     new af9fefc  ipv6: Calculate IPv6 address instead of fetching one from a pool (#3077)
af9fefc is described below

commit af9fefc6b40d1bbd25d6fe306a9b30a8b92d0c94
Author: Wido den Hollander <wi...@widodh.nl>
AuthorDate: Wed Jan 16 19:49:02 2019 +0100

    ipv6: Calculate IPv6 address instead of fetching one from a pool (#3077)
    
    With IPv6 we are not using DHCP to allocate addresses, but using
    StateLess Address Auto Configuration (SLAAC) a Instance will calculate
    it's own address based on the Router Advertisements send out by the
    routers in the network.
    
    This Advertisement contains the IPv6 Subnet in use in that subnet and
    allows to calculate the stable Address the Instance will obtain based
    on it's MAC Address.
    
    The existing code is 'dead code' as it has been written, but was never
    used by any production code.
    
    SLAAC only works properly with subnets of exactly 64-bits large.
    
    Signed-off-by: Wido den Hollander <wi...@widodh.nl>
---
 .../engine/orchestration/NetworkOrchestrator.java  |   4 +-
 .../com/cloud/network/IpAddressManagerImpl.java    |  51 +-------
 .../java/com/cloud/network/Ipv6AddressManager.java |   8 +-
 .../com/cloud/network/Ipv6AddressManagerImpl.java  | 130 +++++++--------------
 .../com/cloud/network/guru/DirectNetworkGuru.java  |  12 --
 .../com/cloud/network/Ipv6AddressManagerTest.java  |  22 ++++
 6 files changed, 71 insertions(+), 156 deletions(-)

diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index f223eb3..9ed99c5 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -2257,8 +2257,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
             }
         }
 
-        if (ipv6 && !NetUtils.isValidIp6Cidr(ip6Cidr)) {
-            throw new InvalidParameterValueException("Invalid IPv6 cidr specified");
+        if (ipv6 && NetUtils.getIp6CidrSize(ip6Cidr) != 64) {
+            throw new InvalidParameterValueException("IPv6 subnet should be exactly 64-bits in size");
         }
 
         //TODO(VXLAN): Support VNI specified
diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
index cb7210d..f840d0d 100644
--- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
@@ -57,7 +57,6 @@ import com.cloud.dc.DataCenterIpAddressVO;
 import com.cloud.dc.HostPodVO;
 import com.cloud.dc.Pod;
 import com.cloud.dc.PodVlanMapVO;
-import com.cloud.dc.Vlan;
 import com.cloud.dc.Vlan.VlanType;
 import com.cloud.dc.VlanVO;
 import com.cloud.dc.dao.AccountVlanMapDao;
@@ -2014,7 +2013,6 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
 
                 if (network.getGateway() != null) {
                     if (nic.getIPv4Address() == null) {
-                        ipv4 = true;
                         PublicIp ip = null;
 
                         //Get ip address from the placeholder and don't allocate a new one
@@ -2050,30 +2048,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
                     nic.setIPv4Dns2(dc.getDns2());
                 }
 
-                //FIXME - get ipv6 address from the placeholder if it's stored there
-                if (network.getIp6Gateway() != null) {
-                    if (nic.getIPv6Address() == null) {
-                        UserIpv6Address ip = _ipv6Mgr.assignDirectIp6Address(dc.getId(), vm.getOwner(), network.getId(), requestedIpv6);
-                        Vlan vlan = _vlanDao.findById(ip.getVlanId());
-                        nic.setIPv6Address(ip.getAddress().toString());
-                        nic.setIPv6Gateway(vlan.getIp6Gateway());
-                        nic.setIPv6Cidr(vlan.getIp6Cidr());
-                        if (ipv4) {
-                            nic.setFormat(AddressFormat.DualStack);
-                        } else {
-                            nic.setIsolationUri(IsolationType.Vlan.toUri(vlan.getVlanTag()));
-                            nic.setBroadcastType(BroadcastDomainType.Vlan);
-                            nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan.getVlanTag()));
-                            nic.setFormat(AddressFormat.Ip6);
-                            nic.setReservationId(String.valueOf(vlan.getVlanTag()));
-                            if(nic.getMacAddress() == null) {
-                                nic.setMacAddress(ip.getMacAddress());
-                            }
-                        }
-                    }
-                    nic.setIPv6Dns1(dc.getIp6Dns1());
-                    nic.setIPv6Dns2(dc.getIp6Dns2());
-                }
+                _ipv6Mgr.setNicIp6Address(nic, dc, network);
             }
         });
     }
@@ -2123,29 +2098,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
                     nic.setIPv4Dns2(dc.getDns2());
                 }
 
-                // TODO: the IPv6 logic is not changed.
-                //FIXME - get ipv6 address from the placeholder if it's stored there
-                if (network.getIp6Gateway() != null) {
-                    if (nic.getIPv6Address() == null) {
-                        UserIpv6Address ip = _ipv6Mgr.assignDirectIp6Address(dc.getId(), vm.getOwner(), network.getId(), requestedIpv6);
-                        Vlan vlan = _vlanDao.findById(ip.getVlanId());
-                        nic.setIPv6Address(ip.getAddress().toString());
-                        nic.setIPv6Gateway(vlan.getIp6Gateway());
-                        nic.setIPv6Cidr(vlan.getIp6Cidr());
-                        if (ipv4) {
-                            nic.setFormat(AddressFormat.DualStack);
-                        } else {
-                            nic.setIsolationUri(IsolationType.Vlan.toUri(vlan.getVlanTag()));
-                            nic.setBroadcastType(BroadcastDomainType.Vlan);
-                            nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan.getVlanTag()));
-                            nic.setFormat(AddressFormat.Ip6);
-                            nic.setReservationId(String.valueOf(vlan.getVlanTag()));
-                            nic.setMacAddress(ip.getMacAddress());
-                        }
-                    }
-                    nic.setIPv6Dns1(dc.getIp6Dns1());
-                    nic.setIPv6Dns2(dc.getIp6Dns2());
-                }
+                _ipv6Mgr.setNicIp6Address(nic, dc, network);
             }
         });
     }
diff --git a/server/src/main/java/com/cloud/network/Ipv6AddressManager.java b/server/src/main/java/com/cloud/network/Ipv6AddressManager.java
index 4db3ec1..7dcba11 100644
--- a/server/src/main/java/com/cloud/network/Ipv6AddressManager.java
+++ b/server/src/main/java/com/cloud/network/Ipv6AddressManager.java
@@ -17,20 +17,20 @@
 
 package com.cloud.network;
 
+import com.cloud.dc.DataCenter;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.user.Account;
 import com.cloud.utils.component.Manager;
+import com.cloud.vm.NicProfile;
 
 public interface Ipv6AddressManager extends Manager {
 
-    public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6) throws InsufficientAddressCapacityException;
-
-    public void revokeDirectIpv6Address(long networkId, String ip6Address);
-
     public String allocateGuestIpv6(Network network, String requestedIpv6) throws InsufficientAddressCapacityException;
 
     public String allocatePublicIp6ForGuestNic(Network network, Long podId, Account ipOwner, String requestedIp) throws InsufficientAddressCapacityException;
 
     public String acquireGuestIpv6Address(Network network, String requestedIpv6) throws InsufficientAddressCapacityException;
 
+    public void setNicIp6Address(final NicProfile nic, final DataCenter dc, final Network network);
+
 }
diff --git a/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java b/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java
index 1cb432e..0371833 100644
--- a/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java
@@ -17,20 +17,18 @@
 
 package com.cloud.network;
 
-import java.util.List;
 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.DataCenterVO;
-import com.cloud.dc.Vlan;
-import com.cloud.dc.VlanVO;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.dc.dao.VlanDao;
 import com.cloud.exception.InsufficientAddressCapacityException;
@@ -39,13 +37,11 @@ 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.NetworkDao;
 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.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.dao.NicSecondaryIpDao;
 import com.cloud.vm.dao.NicSecondaryIpVO;
@@ -65,8 +61,6 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
     @Inject
     UserIpv6AddressDao _ipv6Dao;
     @Inject
-    NetworkDao _networkDao;
-    @Inject
     ConfigurationDao _configDao;
     @Inject
     IpAddressManager ipAddressManager;
@@ -83,87 +77,6 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
         return true;
     }
 
-    @Override
-    public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6) throws InsufficientAddressCapacityException {
-        Network network = _networkDao.findById(networkId);
-        if (network == null) {
-            return null;
-        }
-        List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
-        if (vlans == null) {
-            s_logger.debug("Cannot find related vlan attached to network " + networkId);
-            return null;
-        }
-        String ip = null;
-        Vlan ipVlan = null;
-        if (requestedIp6 == null) {
-            if (!_networkModel.areThereIPv6AddressAvailableInNetwork(networkId)) {
-                throw new InsufficientAddressCapacityException("There is no more address available in the network " + network.getName(), DataCenter.class,
-                    network.getDataCenterId());
-            }
-            for (Vlan vlan : vlans) {
-                if (!_networkModel.isIP6AddressAvailableInVlan(vlan.getId())) {
-                    continue;
-                }
-                ip = NetUtils.getIp6FromRange(vlan.getIp6Range());
-                int count = 0;
-                while (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) != null) {
-                    ip = NetUtils.getNextIp6InRange(ip, vlan.getIp6Range());
-                    count++;
-                    // It's an arbitrate number to prevent the infinite loop
-                    if (count > _ipv6RetryMax) {
-                        ip = null;
-                        break;
-                    }
-                }
-                if (ip != null) {
-                    ipVlan = vlan;
-                }
-            }
-            if (ip == null) {
-                throw new InsufficientAddressCapacityException("Cannot find a usable IP in the network " + network.getName() + " after " + _ipv6RetryMax +
-                    "(network.ipv6.search.retry.max) times retry!", DataCenter.class, network.getDataCenterId());
-            }
-        } else {
-            for (Vlan vlan : vlans) {
-                if (NetUtils.isIp6InRange(requestedIp6, vlan.getIp6Range())) {
-                    ipVlan = vlan;
-                    break;
-                }
-            }
-            if (ipVlan == null) {
-                throw new CloudRuntimeException("Requested IPv6 is not in the predefined range!");
-            }
-            ip = requestedIp6;
-            if (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) != null) {
-                throw new CloudRuntimeException("The requested IP is already taken!");
-            }
-        }
-        DataCenterVO dc = _dcDao.findById(dcId);
-        Long mac = dc.getMacAddress();
-        Long nextMac = mac + 1;
-        dc.setMacAddress(nextMac);
-        _dcDao.update(dc.getId(), dc);
-
-        String macAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(mac, NetworkModel.MACIdentifier.value()));
-        UserIpv6AddressVO ipVO = new UserIpv6AddressVO(ip, dcId, macAddress, ipVlan.getId());
-        ipVO.setPhysicalNetworkId(network.getPhysicalNetworkId());
-        ipVO.setSourceNetworkId(networkId);
-        ipVO.setState(UserIpv6Address.State.Allocated);
-        ipVO.setDomainId(owner.getDomainId());
-        ipVO.setAccountId(owner.getAccountId());
-        _ipv6Dao.persist(ipVO);
-        return ipVO;
-    }
-
-    @Override
-    public void revokeDirectIpv6Address(long networkId, String ip6Address) {
-        UserIpv6AddressVO ip = _ipv6Dao.findByNetworkIdAndIp(networkId, ip6Address);
-        if (ip != null) {
-            _ipv6Dao.remove(ip.getId());
-        }
-    }
-
     /**
      * Executes method {@link #acquireGuestIpv6Address(Network, String)} and returns the requested IPv6 (String) in case of successfully allocating the guest IPv6 address.
      */
@@ -260,4 +173,43 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
         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());
+        }
+    }
+
 }
diff --git a/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java
index 1d8355a..f6279bf 100644
--- a/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java
+++ b/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java
@@ -39,7 +39,6 @@ import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.network.dao.PhysicalNetworkDao;
 import com.cloud.network.dao.PhysicalNetworkVO;
 import com.cloud.network.IpAddressManager;
-import com.cloud.network.Ipv6AddressManager;
 import com.cloud.network.Network;
 import com.cloud.network.Network.GuestType;
 import com.cloud.network.Network.Service;
@@ -55,9 +54,7 @@ import com.cloud.network.PhysicalNetwork.IsolationMethod;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.NetworkVO;
-import com.cloud.network.dao.UserIpv6AddressDao;
 import com.cloud.offering.NetworkOffering;
-import com.cloud.offerings.dao.NetworkOfferingDao;
 import com.cloud.user.Account;
 import com.cloud.utils.component.AdapterBase;
 import com.cloud.utils.db.DB;
@@ -94,12 +91,6 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
     @Inject
     IPAddressDao _ipAddressDao;
     @Inject
-    NetworkOfferingDao _networkOfferingDao;
-    @Inject
-    UserIpv6AddressDao _ipv6Dao;
-    @Inject
-    Ipv6AddressManager _ipv6Mgr;
-    @Inject
     NicSecondaryIpDao _nicSecondaryIpDao;
     @Inject
     NicDao _nicDao;
@@ -367,9 +358,6 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
             }
         }
 
-        if (nic.getIPv6Address() != null) {
-            _ipv6Mgr.revokeDirectIpv6Address(nic.getNetworkId(), nic.getIPv6Address());
-        }
         nic.deallocate();
     }
 
diff --git a/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java b/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java
index 26ca8f4..827f533 100644
--- a/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java
+++ b/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java
@@ -19,6 +19,8 @@ package com.cloud.network;
 
 import static org.mockito.Mockito.mock;
 
+import com.cloud.dc.DataCenter;
+import com.cloud.vm.NicProfile;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -226,4 +228,24 @@ public class Ipv6AddressManagerTest {
         Mockito.when(ipVo.getState()).thenReturn(state);
     }
 
+    @Test
+    public void setNICIPv6AddressTest() {
+        NicProfile nic = new NicProfile();
+        Network network = mock(Network.class);
+        DataCenter dc = mock(DataCenter.class);
+
+        nic.setMacAddress("1e:00:b1:00:0a:f6");
+
+        Mockito.when(network.getIp6Cidr()).thenReturn("2001:db8:100::/64");
+        Mockito.when(network.getIp6Gateway()).thenReturn("2001:db8:100::1");
+
+        Mockito.when(dc.getIp6Dns1()).thenReturn("2001:db8::53:1");
+        Mockito.when(dc.getIp6Dns1()).thenReturn("2001:db8::53:2");
+
+        String expected = "2001:db8:100:0:1c00:b1ff:fe00:af6";
+
+        ip6Manager.setNicIp6Address(nic, dc, network);
+
+        Assert.assertEquals(expected, nic.getIPv6Address());
+    }
 }