Skip to content

Commit af9fefc

Browse files
widoGabrielBrascher
authored andcommitted
ipv6: Calculate IPv6 address instead of fetching one from a pool (apache#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 <[email protected]>
1 parent 093ab72 commit af9fefc

File tree

6 files changed

+71
-156
lines changed

6 files changed

+71
-156
lines changed

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2257,8 +2257,8 @@ public Network createGuestNetwork(final long networkOfferingId, final String nam
22572257
}
22582258
}
22592259

2260-
if (ipv6 && !NetUtils.isValidIp6Cidr(ip6Cidr)) {
2261-
throw new InvalidParameterValueException("Invalid IPv6 cidr specified");
2260+
if (ipv6 && NetUtils.getIp6CidrSize(ip6Cidr) != 64) {
2261+
throw new InvalidParameterValueException("IPv6 subnet should be exactly 64-bits in size");
22622262
}
22632263

22642264
//TODO(VXLAN): Support VNI specified

server/src/main/java/com/cloud/network/IpAddressManagerImpl.java

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
import com.cloud.dc.HostPodVO;
5858
import com.cloud.dc.Pod;
5959
import com.cloud.dc.PodVlanMapVO;
60-
import com.cloud.dc.Vlan;
6160
import com.cloud.dc.Vlan.VlanType;
6261
import com.cloud.dc.VlanVO;
6362
import com.cloud.dc.dao.AccountVlanMapDao;
@@ -2014,7 +2013,6 @@ public void doInTransactionWithoutResult(TransactionStatus status) throws Insuff
20142013

20152014
if (network.getGateway() != null) {
20162015
if (nic.getIPv4Address() == null) {
2017-
ipv4 = true;
20182016
PublicIp ip = null;
20192017

20202018
//Get ip address from the placeholder and don't allocate a new one
@@ -2050,30 +2048,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) throws Insuff
20502048
nic.setIPv4Dns2(dc.getDns2());
20512049
}
20522050

2053-
//FIXME - get ipv6 address from the placeholder if it's stored there
2054-
if (network.getIp6Gateway() != null) {
2055-
if (nic.getIPv6Address() == null) {
2056-
UserIpv6Address ip = _ipv6Mgr.assignDirectIp6Address(dc.getId(), vm.getOwner(), network.getId(), requestedIpv6);
2057-
Vlan vlan = _vlanDao.findById(ip.getVlanId());
2058-
nic.setIPv6Address(ip.getAddress().toString());
2059-
nic.setIPv6Gateway(vlan.getIp6Gateway());
2060-
nic.setIPv6Cidr(vlan.getIp6Cidr());
2061-
if (ipv4) {
2062-
nic.setFormat(AddressFormat.DualStack);
2063-
} else {
2064-
nic.setIsolationUri(IsolationType.Vlan.toUri(vlan.getVlanTag()));
2065-
nic.setBroadcastType(BroadcastDomainType.Vlan);
2066-
nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan.getVlanTag()));
2067-
nic.setFormat(AddressFormat.Ip6);
2068-
nic.setReservationId(String.valueOf(vlan.getVlanTag()));
2069-
if(nic.getMacAddress() == null) {
2070-
nic.setMacAddress(ip.getMacAddress());
2071-
}
2072-
}
2073-
}
2074-
nic.setIPv6Dns1(dc.getIp6Dns1());
2075-
nic.setIPv6Dns2(dc.getIp6Dns2());
2076-
}
2051+
_ipv6Mgr.setNicIp6Address(nic, dc, network);
20772052
}
20782053
});
20792054
}
@@ -2123,29 +2098,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) throws Insuff
21232098
nic.setIPv4Dns2(dc.getDns2());
21242099
}
21252100

2126-
// TODO: the IPv6 logic is not changed.
2127-
//FIXME - get ipv6 address from the placeholder if it's stored there
2128-
if (network.getIp6Gateway() != null) {
2129-
if (nic.getIPv6Address() == null) {
2130-
UserIpv6Address ip = _ipv6Mgr.assignDirectIp6Address(dc.getId(), vm.getOwner(), network.getId(), requestedIpv6);
2131-
Vlan vlan = _vlanDao.findById(ip.getVlanId());
2132-
nic.setIPv6Address(ip.getAddress().toString());
2133-
nic.setIPv6Gateway(vlan.getIp6Gateway());
2134-
nic.setIPv6Cidr(vlan.getIp6Cidr());
2135-
if (ipv4) {
2136-
nic.setFormat(AddressFormat.DualStack);
2137-
} else {
2138-
nic.setIsolationUri(IsolationType.Vlan.toUri(vlan.getVlanTag()));
2139-
nic.setBroadcastType(BroadcastDomainType.Vlan);
2140-
nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan.getVlanTag()));
2141-
nic.setFormat(AddressFormat.Ip6);
2142-
nic.setReservationId(String.valueOf(vlan.getVlanTag()));
2143-
nic.setMacAddress(ip.getMacAddress());
2144-
}
2145-
}
2146-
nic.setIPv6Dns1(dc.getIp6Dns1());
2147-
nic.setIPv6Dns2(dc.getIp6Dns2());
2148-
}
2101+
_ipv6Mgr.setNicIp6Address(nic, dc, network);
21492102
}
21502103
});
21512104
}

server/src/main/java/com/cloud/network/Ipv6AddressManager.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,20 @@
1717

1818
package com.cloud.network;
1919

20+
import com.cloud.dc.DataCenter;
2021
import com.cloud.exception.InsufficientAddressCapacityException;
2122
import com.cloud.user.Account;
2223
import com.cloud.utils.component.Manager;
24+
import com.cloud.vm.NicProfile;
2325

2426
public interface Ipv6AddressManager extends Manager {
2527

26-
public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6) throws InsufficientAddressCapacityException;
27-
28-
public void revokeDirectIpv6Address(long networkId, String ip6Address);
29-
3028
public String allocateGuestIpv6(Network network, String requestedIpv6) throws InsufficientAddressCapacityException;
3129

3230
public String allocatePublicIp6ForGuestNic(Network network, Long podId, Account ipOwner, String requestedIp) throws InsufficientAddressCapacityException;
3331

3432
public String acquireGuestIpv6Address(Network network, String requestedIpv6) throws InsufficientAddressCapacityException;
3533

34+
public void setNicIp6Address(final NicProfile nic, final DataCenter dc, final Network network);
35+
3636
}

server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java

Lines changed: 41 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,18 @@
1717

1818
package com.cloud.network;
1919

20-
import java.util.List;
2120
import java.util.Map;
2221

2322
import javax.inject.Inject;
2423
import javax.naming.ConfigurationException;
2524

25+
import com.cloud.vm.NicProfile;
26+
import com.googlecode.ipv6.IPv6Address;
2627
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
2728
import org.apache.log4j.Logger;
2829

2930
import com.cloud.configuration.Config;
3031
import com.cloud.dc.DataCenter;
31-
import com.cloud.dc.DataCenterVO;
32-
import com.cloud.dc.Vlan;
33-
import com.cloud.dc.VlanVO;
3432
import com.cloud.dc.dao.DataCenterDao;
3533
import com.cloud.dc.dao.VlanDao;
3634
import com.cloud.exception.InsufficientAddressCapacityException;
@@ -39,13 +37,11 @@
3937
import com.cloud.network.Network.IpAddresses;
4038
import com.cloud.network.dao.IPAddressDao;
4139
import com.cloud.network.dao.IPAddressVO;
42-
import com.cloud.network.dao.NetworkDao;
4340
import com.cloud.network.dao.UserIpv6AddressDao;
4441
import com.cloud.user.Account;
4542
import com.cloud.utils.NumbersUtil;
4643
import com.cloud.utils.component.ManagerBase;
4744
import com.cloud.utils.db.DB;
48-
import com.cloud.utils.exception.CloudRuntimeException;
4945
import com.cloud.utils.net.NetUtils;
5046
import com.cloud.vm.dao.NicSecondaryIpDao;
5147
import com.cloud.vm.dao.NicSecondaryIpVO;
@@ -65,8 +61,6 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
6561
@Inject
6662
UserIpv6AddressDao _ipv6Dao;
6763
@Inject
68-
NetworkDao _networkDao;
69-
@Inject
7064
ConfigurationDao _configDao;
7165
@Inject
7266
IpAddressManager ipAddressManager;
@@ -83,87 +77,6 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
8377
return true;
8478
}
8579

86-
@Override
87-
public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6) throws InsufficientAddressCapacityException {
88-
Network network = _networkDao.findById(networkId);
89-
if (network == null) {
90-
return null;
91-
}
92-
List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
93-
if (vlans == null) {
94-
s_logger.debug("Cannot find related vlan attached to network " + networkId);
95-
return null;
96-
}
97-
String ip = null;
98-
Vlan ipVlan = null;
99-
if (requestedIp6 == null) {
100-
if (!_networkModel.areThereIPv6AddressAvailableInNetwork(networkId)) {
101-
throw new InsufficientAddressCapacityException("There is no more address available in the network " + network.getName(), DataCenter.class,
102-
network.getDataCenterId());
103-
}
104-
for (Vlan vlan : vlans) {
105-
if (!_networkModel.isIP6AddressAvailableInVlan(vlan.getId())) {
106-
continue;
107-
}
108-
ip = NetUtils.getIp6FromRange(vlan.getIp6Range());
109-
int count = 0;
110-
while (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) != null) {
111-
ip = NetUtils.getNextIp6InRange(ip, vlan.getIp6Range());
112-
count++;
113-
// It's an arbitrate number to prevent the infinite loop
114-
if (count > _ipv6RetryMax) {
115-
ip = null;
116-
break;
117-
}
118-
}
119-
if (ip != null) {
120-
ipVlan = vlan;
121-
}
122-
}
123-
if (ip == null) {
124-
throw new InsufficientAddressCapacityException("Cannot find a usable IP in the network " + network.getName() + " after " + _ipv6RetryMax +
125-
"(network.ipv6.search.retry.max) times retry!", DataCenter.class, network.getDataCenterId());
126-
}
127-
} else {
128-
for (Vlan vlan : vlans) {
129-
if (NetUtils.isIp6InRange(requestedIp6, vlan.getIp6Range())) {
130-
ipVlan = vlan;
131-
break;
132-
}
133-
}
134-
if (ipVlan == null) {
135-
throw new CloudRuntimeException("Requested IPv6 is not in the predefined range!");
136-
}
137-
ip = requestedIp6;
138-
if (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) != null) {
139-
throw new CloudRuntimeException("The requested IP is already taken!");
140-
}
141-
}
142-
DataCenterVO dc = _dcDao.findById(dcId);
143-
Long mac = dc.getMacAddress();
144-
Long nextMac = mac + 1;
145-
dc.setMacAddress(nextMac);
146-
_dcDao.update(dc.getId(), dc);
147-
148-
String macAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(mac, NetworkModel.MACIdentifier.value()));
149-
UserIpv6AddressVO ipVO = new UserIpv6AddressVO(ip, dcId, macAddress, ipVlan.getId());
150-
ipVO.setPhysicalNetworkId(network.getPhysicalNetworkId());
151-
ipVO.setSourceNetworkId(networkId);
152-
ipVO.setState(UserIpv6Address.State.Allocated);
153-
ipVO.setDomainId(owner.getDomainId());
154-
ipVO.setAccountId(owner.getAccountId());
155-
_ipv6Dao.persist(ipVO);
156-
return ipVO;
157-
}
158-
159-
@Override
160-
public void revokeDirectIpv6Address(long networkId, String ip6Address) {
161-
UserIpv6AddressVO ip = _ipv6Dao.findByNetworkIdAndIp(networkId, ip6Address);
162-
if (ip != null) {
163-
_ipv6Dao.remove(ip.getId());
164-
}
165-
}
166-
16780
/**
16881
* Executes method {@link #acquireGuestIpv6Address(Network, String)} and returns the requested IPv6 (String) in case of successfully allocating the guest IPv6 address.
16982
*/
@@ -260,4 +173,43 @@ protected boolean isIp6Taken(Network network, String requestedIpv6) {
260173
return ip6Vo != null || nicSecondaryIpVO != null;
261174
}
262175

176+
/**
177+
* Calculate the IPv6 Address the Instance will obtain using SLAAC and IPv6 EUI-64
178+
*
179+
* Linux, FreeBSD and Windows all calculate the same IPv6 address when configured properly. (SLAAC)
180+
*
181+
* Using Router Advertisements the routers in the network should announce the IPv6 CIDR which is configured
182+
* for the network.
183+
*
184+
* It is up to the network administrator to make sure the IPv6 Routers in the network are sending out Router Advertisements
185+
* with the correct IPv6 (Prefix, DNS, Lifetime) information.
186+
*
187+
* This way the NIC will be populated with a IPv6 address on which the Instance is reachable.
188+
*
189+
* This method calculates the IPv6 address the Instance will obtain and updates the Nic object with the correct
190+
* address information.
191+
*/
192+
@Override
193+
public void setNicIp6Address(final NicProfile nic, final DataCenter dc, final Network network) {
194+
if (network.getIp6Gateway() != null) {
195+
if (nic.getIPv6Address() == null) {
196+
s_logger.debug("Found IPv6 CIDR " + network.getIp6Cidr() + " for Network " + network);
197+
nic.setIPv6Cidr(network.getIp6Cidr());
198+
nic.setIPv6Gateway(network.getIp6Gateway());
199+
200+
IPv6Address ipv6addr = NetUtils.EUI64Address(network.getIp6Cidr(), nic.getMacAddress());
201+
s_logger.info("Calculated IPv6 address " + ipv6addr + " using EUI-64 for NIC " + nic.getUuid());
202+
nic.setIPv6Address(ipv6addr.toString());
203+
204+
if (nic.getIPv4Address() != null) {
205+
nic.setFormat(Networks.AddressFormat.DualStack);
206+
} else {
207+
nic.setFormat(Networks.AddressFormat.Ip6);
208+
}
209+
}
210+
nic.setIPv6Dns1(dc.getIp6Dns1());
211+
nic.setIPv6Dns2(dc.getIp6Dns2());
212+
}
213+
}
214+
263215
}

server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import com.cloud.network.dao.PhysicalNetworkDao;
4040
import com.cloud.network.dao.PhysicalNetworkVO;
4141
import com.cloud.network.IpAddressManager;
42-
import com.cloud.network.Ipv6AddressManager;
4342
import com.cloud.network.Network;
4443
import com.cloud.network.Network.GuestType;
4544
import com.cloud.network.Network.Service;
@@ -55,9 +54,7 @@
5554
import com.cloud.network.dao.IPAddressDao;
5655
import com.cloud.network.dao.IPAddressVO;
5756
import com.cloud.network.dao.NetworkVO;
58-
import com.cloud.network.dao.UserIpv6AddressDao;
5957
import com.cloud.offering.NetworkOffering;
60-
import com.cloud.offerings.dao.NetworkOfferingDao;
6158
import com.cloud.user.Account;
6259
import com.cloud.utils.component.AdapterBase;
6360
import com.cloud.utils.db.DB;
@@ -94,12 +91,6 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
9491
@Inject
9592
IPAddressDao _ipAddressDao;
9693
@Inject
97-
NetworkOfferingDao _networkOfferingDao;
98-
@Inject
99-
UserIpv6AddressDao _ipv6Dao;
100-
@Inject
101-
Ipv6AddressManager _ipv6Mgr;
102-
@Inject
10394
NicSecondaryIpDao _nicSecondaryIpDao;
10495
@Inject
10596
NicDao _nicDao;
@@ -367,9 +358,6 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
367358
}
368359
}
369360

370-
if (nic.getIPv6Address() != null) {
371-
_ipv6Mgr.revokeDirectIpv6Address(nic.getNetworkId(), nic.getIPv6Address());
372-
}
373361
nic.deallocate();
374362
}
375363

server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
import static org.mockito.Mockito.mock;
2121

22+
import com.cloud.dc.DataCenter;
23+
import com.cloud.vm.NicProfile;
2224
import org.junit.Assert;
2325
import org.junit.Before;
2426
import org.junit.Test;
@@ -226,4 +228,24 @@ private void setAcquireGuestIpv6AddressTest(boolean isIPAvailable, State state)
226228
Mockito.when(ipVo.getState()).thenReturn(state);
227229
}
228230

231+
@Test
232+
public void setNICIPv6AddressTest() {
233+
NicProfile nic = new NicProfile();
234+
Network network = mock(Network.class);
235+
DataCenter dc = mock(DataCenter.class);
236+
237+
nic.setMacAddress("1e:00:b1:00:0a:f6");
238+
239+
Mockito.when(network.getIp6Cidr()).thenReturn("2001:db8:100::/64");
240+
Mockito.when(network.getIp6Gateway()).thenReturn("2001:db8:100::1");
241+
242+
Mockito.when(dc.getIp6Dns1()).thenReturn("2001:db8::53:1");
243+
Mockito.when(dc.getIp6Dns1()).thenReturn("2001:db8::53:2");
244+
245+
String expected = "2001:db8:100:0:1c00:b1ff:fe00:af6";
246+
247+
ip6Manager.setNicIp6Address(nic, dc, network);
248+
249+
Assert.assertEquals(expected, nic.getIPv6Address());
250+
}
229251
}

0 commit comments

Comments
 (0)