MAC address assignment improvements (#12349)

This commit is contained in:
Suresh Kumar Anaparti 2026-01-13 18:26:39 +05:30 committed by GitHub
parent 9e86fdf1b6
commit 6a324da27a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 119 additions and 32 deletions

View File

@ -125,6 +125,10 @@ public interface NetworkModel {
*/
String getNextAvailableMacAddressInNetwork(long networkConfigurationId) throws InsufficientAddressCapacityException;
String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException;
boolean isMACUnique(String mac, long networkId);
PublicIpAddress getPublicIpAddress(long ipAddressId);
List<? extends Vlan> listPodVlans(long podId);
@ -362,4 +366,8 @@ public interface NetworkModel {
boolean checkSecurityGroupSupportForNetwork(Account account, DataCenter zone, List<Long> networkIds,
List<Long> securityGroupsIds);
default long getMacIdentifier(Long dataCenterId) {
return 0;
}
}

View File

@ -40,7 +40,11 @@ public class PublicIp implements PublicIpAddress {
}
public static PublicIp createFromAddrAndVlan(IPAddressVO addr, VlanVO vlan) {
return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), NetworkModel.MACIdentifier.value()));
long macIdentifier = NetworkModel.MACIdentifier.valueIn(addr.getDataCenterId());
if (macIdentifier == 0) {
macIdentifier = addr.getDataCenterId();
}
return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), macIdentifier));
}
@Override
@ -274,6 +278,4 @@ public class PublicIp implements PublicIpAddress {
public boolean isForSystemVms() {
return false;
}
}

View File

@ -1270,7 +1270,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
nicProfile.setIPv4Gateway(ipv4Gateway);
nicProfile.setIPv4Netmask(ipv4Netmask);
if (nicProfile.getMacAddress() == null) {
if (nicProfile.getMacAddress() == null || !_networkModel.isMACUnique(nicProfile.getMacAddress(), network.getId())) {
try {
String macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId());
nicProfile.setMacAddress(macAddress);

View File

@ -396,6 +396,7 @@ public class NetworkOrchestratorTest extends TestCase {
when(testOrchestrator._ipAddressDao.acquireInLockTable(Mockito.anyLong())).thenReturn(ipVoSpy);
when(testOrchestrator._ipAddressDao.update(Mockito.anyLong(), Mockito.any(IPAddressVO.class))).thenReturn(true);
when(testOrchestrator._ipAddressDao.releaseFromLockTable(Mockito.anyLong())).thenReturn(true);
when(testOrchestrator._networkModel.isMACUnique(Mockito.anyString(), Mockito.anyLong())).thenReturn(true);
try {
when(testOrchestrator._networkModel.getNextAvailableMacAddressInNetwork(Mockito.anyLong())).thenReturn(macAddress);
} catch (InsufficientAddressCapacityException e) {

View File

@ -432,8 +432,8 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
public String getNextAvailableMacAddress(final long networkConfigId, Integer zoneMacIdentifier) {
final SequenceFetcher fetch = SequenceFetcher.getInstance();
long seq = fetch.getNextSequence(Long.class, _tgMacAddress, networkConfigId);
if(zoneMacIdentifier != null && zoneMacIdentifier.intValue() != 0 ){
seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000l;
if (zoneMacIdentifier != null && zoneMacIdentifier != 0) {
seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000L;
}
return NetUtils.long2Mac(seq);
}

View File

@ -91,7 +91,7 @@ public interface NicDao extends GenericDao<NicVO, Long> {
List<NicVO> listByVmIdAndKeyword(long instanceId, String keyword);
NicVO findByMacAddress(String macAddress);
NicVO findByMacAddress(String macAddress, long networkId);
NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac);

View File

@ -400,9 +400,10 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
}
@Override
public NicVO findByMacAddress(String macAddress) {
public NicVO findByMacAddress(String macAddress, long networkId) {
SearchCriteria<NicVO> sc = AllFieldsSearch.create();
sc.setParameters("macAddress", macAddress);
sc.setParameters("network", networkId);
return findOneBy(sc);
}

View File

@ -2372,7 +2372,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
nic.setBroadcastUri(network.getBroadcastUri());
nic.setFormat(AddressFormat.Ip4);
if(nic.getMacAddress() == null) {
if (nic.getMacAddress() == null || !_networkModel.isMACUnique(nic.getMacAddress(), network.getId())) {
nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId()));
}
}

View File

@ -241,7 +241,7 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
if (nic.getBroadCastUri() == null) {
nic.setBroadcastUri(network.getBroadcastUri());
}
if (nic.getMacAddress() == null) {
if (nic.getMacAddress() == null || !_networkModel.isMACUnique(nic.getMacAddress(), network.getId())) {
nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId()));
}
}

View File

@ -593,22 +593,34 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
@Override
public String getNextAvailableMacAddressInNetwork(long networkId) throws InsufficientAddressCapacityException {
NetworkVO network = _networksDao.findById(networkId);
Integer zoneIdentifier = MACIdentifier.value();
if (zoneIdentifier.intValue() == 0) {
zoneIdentifier = Long.valueOf(network.getDataCenterId()).intValue();
if (network == null) {
throw new CloudRuntimeException("Could not find network with id " + networkId);
}
Integer zoneMacIdentifier = Long.valueOf(getMacIdentifier(network.getDataCenterId())).intValue();
String mac;
do {
mac = _networksDao.getNextAvailableMacAddress(networkId, zoneIdentifier);
mac = _networksDao.getNextAvailableMacAddress(networkId, zoneMacIdentifier);
if (mac == null) {
throw new InsufficientAddressCapacityException("Unable to create another mac address", Network.class, networkId);
}
} while(! isMACUnique(mac));
} while (!isMACUnique(mac, networkId));
return mac;
}
private boolean isMACUnique(String mac) {
return (_nicDao.findByMacAddress(mac) == null);
@Override
public String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException {
String macAddressStr = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(macAddress, getMacIdentifier(datacenterId)));
if (!isMACUnique(macAddressStr, networkId)) {
macAddressStr = getNextAvailableMacAddressInNetwork(networkId);
}
return macAddressStr;
}
@Override
public boolean isMACUnique(String mac, long networkId) {
return (_nicDao.findByMacAddress(mac, networkId) == null);
}
@Override
@ -2815,4 +2827,18 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
}
return false;
}
@Override
public long getMacIdentifier(Long dataCenterId) {
long macAddress = 0;
if (dataCenterId == null) {
macAddress = NetworkModel.MACIdentifier.value();
} else {
macAddress = NetworkModel.MACIdentifier.valueIn(dataCenterId);
if (macAddress == 0) {
macAddress = dataCenterId;
}
}
return macAddress;
}
}

View File

@ -293,7 +293,7 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
allocateDirectIp(nic, network, vm, dc, nic.getRequestedIPv4(), nic.getRequestedIPv6());
nic.setReservationStrategy(ReservationStrategy.Create);
if (nic.getMacAddress() == null) {
if (nic.getMacAddress() == null || !_networkModel.isMACUnique(nic.getMacAddress(), network.getId())) {
nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId()));
if (nic.getMacAddress() == null) {
throw new InsufficientAddressCapacityException("Unable to allocate more mac addresses", Network.class, network.getId());

View File

@ -53,6 +53,8 @@ public class PodBasedNetworkGuru extends AdapterBase implements NetworkGuru {
DataCenterDao _dcDao;
@Inject
StorageNetworkManager _sNwMgr;
@Inject
NetworkModel _networkModel;
Random _rand = new Random(System.currentTimeMillis());
@ -131,7 +133,8 @@ public class PodBasedNetworkGuru extends AdapterBase implements NetworkGuru {
Integer vlan = result.getVlan();
nic.setIPv4Address(result.getIpAddress());
nic.setMacAddress(NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(result.getMacAddress(), NetworkModel.MACIdentifier.value())));
String macAddress = _networkModel.getUniqueMacAddress(result.getMacAddress(), config.getId(), dest.getDataCenter().getId());
nic.setMacAddress(macAddress);
nic.setIPv4Gateway(pod.getGateway());
nic.setFormat(AddressFormat.Ip4);
String netmask = NetUtils.getCidrNetmask(pod.getCidrSize());

View File

@ -189,8 +189,12 @@ public class PrivateNetworkGuru extends AdapterBase implements NetworkGuru {
PrivateIpVO ipVO = _privateIpDao.allocateIpAddress(network.getDataCenterId(), network.getId(), null);
String vlanTag = BroadcastDomainType.getValue(network.getBroadcastUri());
String netmask = NetUtils.getCidrNetmask(network.getCidr());
String macAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), networkModel.getMacIdentifier(network.getDataCenterId())));
if (!networkModel.isMACUnique(macAddress, network.getId())) {
macAddress = networkModel.getNextAvailableMacAddressInNetwork(network.getId());
}
PrivateIpAddress ip =
new PrivateIpAddress(ipVO, vlanTag, network.getGateway(), netmask, NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), NetworkModel.MACIdentifier.value())));
new PrivateIpAddress(ipVO, vlanTag, network.getGateway(), netmask, macAddress);
nic.setIPv4Address(ip.getIpAddress());
nic.setIPv4Gateway(ip.getGateway());

View File

@ -37,7 +37,6 @@ import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offering.NetworkOffering;
import com.cloud.user.Account;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.Nic.ReservationStrategy;
import com.cloud.vm.NicProfile;
import com.cloud.vm.ReservationContext;
@ -47,6 +46,8 @@ public class StorageNetworkGuru extends PodBasedNetworkGuru implements NetworkGu
@Inject
StorageNetworkManager _sNwMgr;
@Inject
NetworkModel _networkModel;
@Inject
NetworkDao _nwDao;
protected StorageNetworkGuru() {
@ -130,7 +131,8 @@ public class StorageNetworkGuru extends PodBasedNetworkGuru implements NetworkGu
vlan = ip.getVlan();
nic.setIPv4Address(ip.getIpAddress());
nic.setMacAddress(NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ip.getMac(), NetworkModel.MACIdentifier.value())));
String macAddress = _networkModel.getUniqueMacAddress(ip.getMac(), network.getId(), dest.getDataCenter().getId());
nic.setMacAddress(macAddress);
nic.setFormat(AddressFormat.Ip4);
nic.setIPv4Netmask(ip.getNetmask());
nic.setBroadcastType(BroadcastDomainType.Storage);

View File

@ -16,6 +16,7 @@
// under the License.
package com.cloud.network.router;
import com.cloud.exception.InsufficientAddressCapacityException;
import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
import com.cloud.network.Network;
@ -24,7 +25,7 @@ import com.cloud.vm.NicProfile;
public interface NicProfileHelper {
public abstract NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router);
public abstract NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router) throws InsufficientAddressCapacityException;
public abstract NicProfile createGuestNicProfileForVpcRouter(final RouterDeploymentDefinition vpcRouterDeploymentDefinition,
Network guestNetwork);

View File

@ -21,6 +21,7 @@ import java.net.URI;
import javax.inject.Inject;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.vm.NicVO;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
@ -61,7 +62,7 @@ public class NicProfileHelperImpl implements NicProfileHelper {
@Override
@DB
public NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router) {
public NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router) throws InsufficientAddressCapacityException {
final Network privateNetwork = _networkModel.getNetwork(privateGateway.getNetworkId());
PrivateIpVO ipVO = _privateIpDao.allocateIpAddress(privateNetwork.getDataCenterId(), privateNetwork.getId(), privateGateway.getIp4Address());
@ -90,14 +91,14 @@ public class NicProfileHelperImpl implements NicProfileHelper {
privateNicProfile.setDeviceId(null);
if (router.getIsRedundantRouter()) {
String newMacAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), NetworkModel.MACIdentifier.value()));
String newMacAddress = _networkModel.getUniqueMacAddress(ipVO.getMacAddress(), privateNetwork.getId(), privateNetwork.getDataCenterId());
privateNicProfile.setMacAddress(newMacAddress);
}
} else {
final String netmask = NetUtils.getCidrNetmask(privateNetwork.getCidr());
String newMacAddress = _networkModel.getUniqueMacAddress(ipVO.getMacAddress(), privateNetwork.getId(), privateNetwork.getDataCenterId());
final PrivateIpAddress ip =
new PrivateIpAddress(ipVO, privateNetwork.getBroadcastUri().toString(), privateNetwork.getGateway(), netmask,
NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), NetworkModel.MACIdentifier.value())));
new PrivateIpAddress(ipVO, privateNetwork.getBroadcastUri().toString(), privateNetwork.getGateway(), netmask, newMacAddress);
final URI netUri = BroadcastDomainType.fromString(ip.getBroadcastUri());
privateNicProfile.setIPv4Address(ip.getIpAddress());

View File

@ -246,8 +246,10 @@ public class Ipv6AddressManagerTest {
Mockito.when(network.getIp6Cidr()).thenReturn("2001:db8:100::/64");
Mockito.when(network.getIp6Gateway()).thenReturn("2001:db8:100::1");
Mockito.when(network.getId()).thenReturn(1L);
Mockito.when(networkModel.getNetworkIp6Dns(network, dc)).thenReturn(new Pair<>("2001:db8::53:1", "2001:db8::53:2"));
Mockito.when(networkModel.isMACUnique("1e:00:b1:00:0a:f6", 1L)).thenReturn(true);
String expected = "2001:db8:100:0:1c00:b1ff:fe00:af6";

View File

@ -129,6 +129,24 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getUniqueMacAddress(long, long, long)
*/
@Override
public String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#isMACUnique(String, long)
*/
@Override
public boolean isMACUnique(String mac, long networkId) {
// TODO Auto-generated method stub
return true;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getPublicIpAddress(long)
*/

View File

@ -141,6 +141,24 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getUniqueMacAddress(long, long, long)
*/
@Override
public String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#isMACUnique(String, long)
*/
@Override
public boolean isMACUnique(String mac, long networkId) {
// TODO Auto-generated method stub
return true;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getPublicIpAddress(long)
*/

View File

@ -131,17 +131,17 @@ public class NetUtils {
}
}
public static long createSequenceBasedMacAddress(final long macAddress, long globalConfig) {
public static long createSequenceBasedMacAddress(final long macAddress, long macIdentifier) {
/*
Logic for generating MAC address:
Mac = B1:B2:B3:B4:B5:B6 (Bx is a byte).
B1 -> Presently controlled by prefix variable. The value should be such that the MAC is local and unicast.
B2 -> This will be configurable for each deployment/installation. Controlled by the global config MACIdentifier
B2 -> This will be configurable for each deployment/installation. Controlled by the 'mac.identifier' zone-level config
B3 -> A randomly generated number between 0 - 255
B4,5,6 -> These bytes are based on the unique DB identifier associated with the IP address for which MAC is generated (refer to mac_address field in user_ip_address table).
*/
return macAddress | prefix<<40 | globalConfig << 32 & 0x00ff00000000l | (long)s_rand.nextInt(255) << 24;
return macAddress | prefix << 40 | macIdentifier << 32 & 0x00ff00000000L | (long)s_rand.nextInt(255) << 24;
}
public static String getHostName() {