You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2019/07/25 06:43:52 UTC

[cloudstack] branch master updated: kvm: Allow Link Local Cidr (cloud0 interface) to be configured (#3500)

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

rohit 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 e894658  kvm: Allow Link Local Cidr (cloud0 interface) to be configured (#3500)
e894658 is described below

commit e894658f8c3584f572a5e6c5fe292a2bae03c79c
Author: Wido den Hollander <wi...@widodh.nl>
AuthorDate: Thu Jul 25 08:43:39 2019 +0200

    kvm: Allow Link Local Cidr (cloud0 interface) to be configured (#3500)
    
    There are certain scenarios where the 169.254.0.0/16 subnet is used for different
    purposes then CloudStack on a hypervisor.
    
    Once of such scenarios is a BGP+EVPN+VXLAN setup using BGP Unnumbered where the
    169.254.0.1 address is used by Frr/Zebra BGP routing to send traffic to the
    neighboring router.
    
    The following settings can be changed in the agent.properties (default values added):
    
    control.cidr=169.254.0.0/16
    
    Make sure the global setting 'control.cidr' matches the values defined in the agent.propeties!
    
    In the future the mgmt server can send this parameter to a KVM Agent on startup, but at the moment
    this framework is not in place and thus these values can't be send to the Agent in a proper manner.
    
    Signed-off-by: Wido den Hollander <wi...@widodh.nl>
---
 .../hypervisor/kvm/resource/BridgeVifDriver.java   | 28 ++++++++++--------
 .../hypervisor/kvm/resource/IvsVifDriver.java      | 20 +++++++------
 .../hypervisor/kvm/resource/OvsVifDriver.java      | 16 +++++++----
 .../configuration/ConfigurationManagerImpl.java    | 16 +----------
 .../com/cloud/network/guru/ControlNetworkGuru.java | 11 ++++++--
 .../com/cloud/server/ConfigurationServerImpl.java  |  8 ++----
 .../main/java/com/cloud/test/IPRangeConfig.java    |  2 +-
 .../main/java/com/cloud/utils/net/NetUtils.java    | 33 +++++++++++++---------
 .../java/com/cloud/utils/net/NetUtilsTest.java     | 22 +++++++++++++++
 9 files changed, 93 insertions(+), 63 deletions(-)

diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
index ebaf23f..d7bb763 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
@@ -28,17 +28,17 @@ import java.util.regex.Pattern;
 
 import javax.naming.ConfigurationException;
 
+import com.cloud.utils.StringUtils;
+import com.cloud.utils.net.NetUtils;
+import com.cloud.utils.script.OutputInterpreter;
+import com.google.common.base.Strings;
 import org.apache.log4j.Logger;
 import org.libvirt.LibvirtException;
 
-import com.google.common.base.Strings;
-
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.exception.InternalErrorException;
 import com.cloud.network.Networks;
 import com.cloud.utils.NumbersUtil;
-import com.cloud.utils.net.NetUtils;
-import com.cloud.utils.script.OutputInterpreter;
 import com.cloud.utils.script.Script;
 
 public class BridgeVifDriver extends VifDriverBase {
@@ -49,7 +49,7 @@ public class BridgeVifDriver extends VifDriverBase {
     private final Object _vnetBridgeMonitor = new Object();
     private String _modifyVlanPath;
     private String _modifyVxlanPath;
-    private String bridgeNameSchema;
+    private String _controlCidr = NetUtils.getLinkLocalCIDR();
     private Long libvirtVersion;
 
     @Override
@@ -67,7 +67,10 @@ public class BridgeVifDriver extends VifDriverBase {
             networkScriptsDir = "scripts/vm/network/vnet";
         }
 
-        bridgeNameSchema = (String)params.get("network.bridge.name.schema");
+        String controlCidr = (String)params.get("control.cidr");
+        if (StringUtils.isNotBlank(controlCidr)) {
+            _controlCidr = controlCidr;
+        }
 
         String value = (String)params.get("scripts.timeout");
         _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000;
@@ -384,7 +387,7 @@ public class BridgeVifDriver extends VifDriverBase {
     private void deleteExistingLinkLocalRouteTable(String linkLocalBr) {
         Script command = new Script("/bin/bash", _timeout);
         command.add("-c");
-        command.add("ip route | grep " + NetUtils.getLinkLocalCIDR());
+        command.add("ip route | grep " + _controlCidr);
         OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
         String result = command.execute(parser);
         boolean foundLinkLocalBr = false;
@@ -397,15 +400,16 @@ public class BridgeVifDriver extends VifDriverBase {
                 }
                 final String device = tokens[2];
                 if (!Strings.isNullOrEmpty(device) && !device.equalsIgnoreCase(linkLocalBr)) {
-                    Script.runSimpleBashScript("ip route del " + NetUtils.getLinkLocalCIDR() + " dev " + tokens[2]);
+                    Script.runSimpleBashScript("ip route del " + _controlCidr + " dev " + tokens[2]);
                 } else {
                     foundLinkLocalBr = true;
                 }
             }
         }
+
         if (!foundLinkLocalBr) {
-            Script.runSimpleBashScript("ip address add 169.254.0.1/16 dev " + linkLocalBr + ";" + "ip route add " + NetUtils.getLinkLocalCIDR() + " dev " + linkLocalBr + " src " +
-                    NetUtils.getLinkLocalGateway());
+            Script.runSimpleBashScript("ip address add " + NetUtils.getLinkLocalAddressFromCIDR(_controlCidr) + " dev " + linkLocalBr);
+            Script.runSimpleBashScript("ip route add " + _controlCidr + " dev " + linkLocalBr + " src " + NetUtils.getLinkLocalGateway(_controlCidr));
         }
     }
 
@@ -417,7 +421,9 @@ public class BridgeVifDriver extends VifDriverBase {
     public void createControlNetwork(String privBrName)  {
         deleteExistingLinkLocalRouteTable(privBrName);
         if (!isExistingBridge(privBrName)) {
-            Script.runSimpleBashScript("brctl addbr " + privBrName + "; ip link set " + privBrName + " up; ip address add 169.254.0.1/16 dev " + privBrName, _timeout);
+            Script.runSimpleBashScript("ip link add name " + privBrName + " type bridge");
+            Script.runSimpleBashScript("ip link set " + privBrName + " up");
+            Script.runSimpleBashScript("ip address add " + NetUtils.getLinkLocalAddressFromCIDR(_controlCidr) + " dev " + privBrName);
         }
     }
 
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java
index 4ba0114..857a360 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java
@@ -26,6 +26,7 @@ import java.util.regex.Pattern;
 
 import javax.naming.ConfigurationException;
 
+import com.cloud.utils.StringUtils;
 import org.apache.log4j.Logger;
 import org.libvirt.LibvirtException;
 
@@ -46,7 +47,7 @@ public class IvsVifDriver extends VifDriverBase {
     private String _modifyVlanPath;
     private String _modifyVxlanPath;
     private String _ivsIfUpPath;
-    private Long libvirtVersion;
+    private String _controlCidr = NetUtils.getLinkLocalCIDR();
 
     @Override
     public void configure(Map<String, Object> params) throws ConfigurationException {
@@ -70,9 +71,9 @@ public class IvsVifDriver extends VifDriverBase {
         }
         _ivsIfUpPath = Script.findScript(utilScriptsDir, "qemu-ivs-ifup");
 
-        libvirtVersion = (Long) params.get("libvirtVersion");
-        if (libvirtVersion == null) {
-            libvirtVersion = 0L;
+        String controlCidr = (String)params.get("control.cidr");
+        if (StringUtils.isNotBlank(controlCidr)) {
+            _controlCidr = controlCidr;
         }
     }
 
@@ -256,7 +257,7 @@ public class IvsVifDriver extends VifDriverBase {
     private void deleteExitingLinkLocalRouteTable(String linkLocalBr) {
         Script command = new Script("/bin/bash", _timeout);
         command.add("-c");
-        command.add("ip route | grep " + NetUtils.getLinkLocalCIDR());
+        command.add("ip route | grep " + _controlCidr);
         OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
         String result = command.execute(parser);
         boolean foundLinkLocalBr = false;
@@ -265,15 +266,15 @@ public class IvsVifDriver extends VifDriverBase {
             for (String line : lines) {
                 String[] tokens = line.split(" ");
                 if (!tokens[2].equalsIgnoreCase(linkLocalBr)) {
-                    Script.runSimpleBashScript("ip route del " + NetUtils.getLinkLocalCIDR());
+                    Script.runSimpleBashScript("ip route del " + _controlCidr);
                 } else {
                     foundLinkLocalBr = true;
                 }
             }
         }
         if (!foundLinkLocalBr) {
-            Script.runSimpleBashScript("ip address add 169.254.0.1/16 dev " + linkLocalBr + ";" + "ip route add " + NetUtils.getLinkLocalCIDR() + " dev " + linkLocalBr + " src " +
-                NetUtils.getLinkLocalGateway());
+            Script.runSimpleBashScript("ip address add " + NetUtils.getLinkLocalAddressFromCIDR(_controlCidr) + " dev " + linkLocalBr);
+            Script.runSimpleBashScript("ip route add " + _controlCidr + " dev " + linkLocalBr + " src " + NetUtils.getLinkLocalGateway(_controlCidr));
         }
     }
 
@@ -281,7 +282,8 @@ public class IvsVifDriver extends VifDriverBase {
     public void createControlNetwork(String privBrName) {
         deleteExitingLinkLocalRouteTable(privBrName);
         if (!isBridgeExists(privBrName)) {
-            Script.runSimpleBashScript("brctl addbr " + privBrName + "; ip link set " + privBrName + " up; ip address add 169.254.0.1/16 dev " + privBrName, _timeout);
+            Script.runSimpleBashScript("brctl addbr " + privBrName + "; ip link set " + privBrName + " up");
+            Script.runSimpleBashScript("ip address add " + NetUtils.getLinkLocalAddressFromCIDR(_controlCidr) + " dev " + privBrName, _timeout);
         }
     }
 
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
index 1e7f4d5..7bf35e6 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
@@ -44,6 +44,7 @@ import com.cloud.utils.script.Script;
 public class OvsVifDriver extends VifDriverBase {
     private static final Logger s_logger = Logger.getLogger(OvsVifDriver.class);
     private int _timeout;
+    private String _controlCidr = NetUtils.getLinkLocalCIDR();
     private DpdkDriver dpdkDriver;
 
     @Override
@@ -62,6 +63,11 @@ public class OvsVifDriver extends VifDriverBase {
             dpdkDriver = new DpdkDriverImpl();
         }
 
+        String controlCidr = (String)params.get("control.cidr");
+        if (com.cloud.utils.StringUtils.isNotBlank(controlCidr)) {
+            _controlCidr = controlCidr;
+        }
+
         String value = (String)params.get("scripts.timeout");
         _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000;
     }
@@ -213,7 +219,7 @@ public class OvsVifDriver extends VifDriverBase {
     private void deleteExitingLinkLocalRouteTable(String linkLocalBr) {
         Script command = new Script("/bin/bash", _timeout);
         command.add("-c");
-        command.add("ip route | grep " + NetUtils.getLinkLocalCIDR());
+        command.add("ip route | grep " + _controlCidr);
         OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
         String result = command.execute(parser);
         boolean foundLinkLocalBr = false;
@@ -222,15 +228,15 @@ public class OvsVifDriver extends VifDriverBase {
             for (String line : lines) {
                 String[] tokens = line.split(" ");
                 if (!tokens[2].equalsIgnoreCase(linkLocalBr)) {
-                    Script.runSimpleBashScript("ip route del " + NetUtils.getLinkLocalCIDR());
+                    Script.runSimpleBashScript("ip route del " + _controlCidr);
                 } else {
                     foundLinkLocalBr = true;
                 }
             }
         }
         if (!foundLinkLocalBr) {
-            Script.runSimpleBashScript("ip address add 169.254.0.1/16 dev " + linkLocalBr + ";" + "ip route add " + NetUtils.getLinkLocalCIDR() + " dev " + linkLocalBr + " src " +
-                    NetUtils.getLinkLocalGateway());
+            Script.runSimpleBashScript("ip address add " + NetUtils.getLinkLocalAddressFromCIDR(_controlCidr) + " dev " + linkLocalBr + ";" + "ip route add " + _controlCidr + " dev " + linkLocalBr + " src " +
+                    NetUtils.getLinkLocalGateway(_controlCidr));
         }
     }
 
@@ -238,7 +244,7 @@ public class OvsVifDriver extends VifDriverBase {
     public void createControlNetwork(String privBrName) {
         deleteExitingLinkLocalRouteTable(privBrName);
         if (!isExistingBridge(privBrName)) {
-            Script.runSimpleBashScript("ovs-vsctl add-br " + privBrName + "; ip link set " + privBrName + " up; ip address add 169.254.0.1/16 dev " + privBrName, _timeout);
+            Script.runSimpleBashScript("ovs-vsctl add-br " + privBrName + "; ip link set " + privBrName + " up; ip address add " + NetUtils.getLinkLocalAddressFromCIDR(_controlCidr) + " dev " + privBrName, _timeout);
         }
     }
 
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 2c71f0d..288c5df 100755
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -1613,7 +1613,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                     _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal, false, null);
                 }
 
-                final String[] linkLocalIpRanges = getLinkLocalIPRange();
+                final String[] linkLocalIpRanges = NetUtils.getLinkLocalIPRange(_configDao.getValue(Config.ControlCidr.key()));
                 if (linkLocalIpRanges != null) {
                     _zoneDao.addLinkLocalIpAddress(zoneId, pod.getId(), linkLocalIpRanges[0], linkLocalIpRanges[1]);
                 }
@@ -4489,20 +4489,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         }
     }
 
-    private String[] getLinkLocalIPRange() {
-        final String ipNums = _configDao.getValue("linkLocalIp.nums");
-        final int nums = Integer.parseInt(ipNums);
-        if (nums > 16 || nums <= 0) {
-            throw new InvalidParameterValueException("The linkLocalIp.nums: " + nums + "is wrong, should be 1~16");
-        }
-        /* local link ip address starts from 169.254.0.2 - 169.254.(nums) */
-        final String[] ipRanges = NetUtils.getLinkLocalIPRange(nums);
-        if (ipRanges == null) {
-            throw new InvalidParameterValueException("The linkLocalIp.nums: " + nums + "may be wrong, should be 1~16");
-        }
-        return ipRanges;
-    }
-
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_DELETE, eventDescription = "deleting vlan ip range", async = false)
     public boolean deleteVlanIpRange(final DeleteVlanIpRangeCmd cmd) {
diff --git a/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java
index 87afb9f..717b3bd 100644
--- a/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java
+++ b/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java
@@ -160,11 +160,16 @@ public class ControlNetworkGuru extends PodBasedNetworkGuru implements NetworkGu
         if (ip == null) {
             throw new InsufficientAddressCapacityException("Insufficient link local address capacity", DataCenter.class, dest.getDataCenter().getId());
         }
+
+        String netmask = NetUtils.cidr2Netmask(_cidr);
+
+        s_logger.debug(String.format("Reserved NIC for %s [ipv4:%s netmask:%s gateway:%s]", vm.getInstanceName(), ip, netmask, _gateway));
+
         nic.setIPv4Address(ip);
         nic.setMacAddress(NetUtils.long2Mac(NetUtils.ip2Long(ip) | (14l << 40)));
-        nic.setIPv4Netmask("255.255.0.0");
+        nic.setIPv4Netmask(netmask);
         nic.setFormat(AddressFormat.Ip4);
-        nic.setIPv4Gateway(NetUtils.getLinkLocalGateway());
+        nic.setIPv4Gateway(_gateway);
     }
 
     @Override
@@ -223,7 +228,7 @@ public class ControlNetworkGuru extends PodBasedNetworkGuru implements NetworkGu
 
         _cidr = dbParams.get(Config.ControlCidr.toString());
         if (_cidr == null) {
-            _cidr = "169.254.0.0/16";
+            _cidr = NetUtils.getLinkLocalCIDR();
         }
 
         _gateway = dbParams.get(Config.ControlGateway.toString());
diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index 88a0dc3..635b482 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -904,12 +904,8 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
                         throw new InvalidParameterValueException("The linkLocalIp.nums: " + nums + "is wrong, should be 1~16");
                     }
                     /* local link ip address starts from 169.254.0.2 - 169.254.(nums) */
-                    String[] linkLocalIpRanges = NetUtils.getLinkLocalIPRange(nums);
-                    if (linkLocalIpRanges == null) {
-                        throw new InvalidParameterValueException("The linkLocalIp.nums: " + nums + "may be wrong, should be 1~16");
-                    } else {
-                        _zoneDao.addLinkLocalIpAddress(zoneId, pod.getId(), linkLocalIpRanges[0], linkLocalIpRanges[1]);
-                    }
+                    String[] linkLocalIpRanges = NetUtils.getLinkLocalIPRange(_configDao.getValue(Config.ControlCidr.key()));
+                    _zoneDao.addLinkLocalIpAddress(zoneId, pod.getId(), linkLocalIpRanges[0], linkLocalIpRanges[1]);
                 }
             });
         } catch (Exception e) {
diff --git a/server/src/main/java/com/cloud/test/IPRangeConfig.java b/server/src/main/java/com/cloud/test/IPRangeConfig.java
index 020c828..f359891 100644
--- a/server/src/main/java/com/cloud/test/IPRangeConfig.java
+++ b/server/src/main/java/com/cloud/test/IPRangeConfig.java
@@ -436,7 +436,7 @@ public class IPRangeConfig {
             problemIPs = savePrivateIPRange(txn, startIPLong, endIPLong, podId, zoneId);
         }
 
-        String[] linkLocalIps = NetUtils.getLinkLocalIPRange(10);
+        String[] linkLocalIps = NetUtils.getLinkLocalIPRange("169.254.0.0/16");
         long startLinkLocalIp = NetUtils.ip2Long(linkLocalIps[0]);
         long endLinkLocalIp = NetUtils.ip2Long(linkLocalIps[1]);
 
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 100986a..fca35ad 100644
--- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java
+++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
@@ -961,27 +961,34 @@ public class NetUtils {
         return "255.255.0.0";
     }
 
+    public static String getLinkLocalGateway(String cidr) {
+        return getLinkLocalFirstAddressFromCIDR(cidr);
+    }
+
     public static String getLinkLocalGateway() {
-        return "169.254.0.1";
+        return getLinkLocalGateway(getLinkLocalCIDR());
     }
 
     public static String getLinkLocalCIDR() {
         return "169.254.0.0/16";
     }
 
-    public static String[] getLinkLocalIPRange(final int size) {
-        if (size > 16 || size <= 0) {
-            return null;
-        }
-        /* reserve gateway */
-        final String[] range = getIpRangeFromCidr(getLinkLocalGateway(), MAX_CIDR - size);
+    public static String getLinkLocalFirstAddressFromCIDR(final String cidr) {
+        SubnetUtils subnetUtils = new SubnetUtils(cidr);
+        return subnetUtils.getInfo().getLowAddress();
+    }
+
+    public static String getLinkLocalAddressFromCIDR(final String cidr) {
+        return getLinkLocalFirstAddressFromCIDR(cidr) + "/" + cidr2Netmask(cidr);
+    }
+
+    public static String[] getLinkLocalIPRange(final String cidr) {
+        final SubnetUtils subnetUtils = new SubnetUtils(cidr);
+        final String[] addresses = subnetUtils.getInfo().getAllAddresses();
+        final String[] range = new String[2];
+        range[0] = addresses[1];
+        range[1] = subnetUtils.getInfo().getHighAddress();
 
-        if (range[0].equalsIgnoreCase(getLinkLocalGateway())) {
-            /* remove the gateway */
-            long ip = ip2Long(range[0]);
-            ip += 1;
-            range[0] = long2Ip(ip);
-        }
         return range;
     }
 
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 173704a..0ac1032 100644
--- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
@@ -709,4 +709,26 @@ public class NetUtilsTest {
         assertFalse(NetUtils.isIPv6EUI64("2001:db8::100:1"));
         assertFalse(NetUtils.isIPv6EUI64("2a01:4f9:2a:185f::2"));
     }
+
+    @Test
+    public void testLinkLocal() {
+        final String cidr = NetUtils.getLinkLocalCIDR();
+        assertEquals("255.255.0.0", NetUtils.getLinkLocalNetMask());
+        assertEquals("169.254.0.1", NetUtils.getLinkLocalGateway());
+        assertEquals("169.254.0.0/16", cidr);
+        assertEquals("169.254.0.1", NetUtils.getLinkLocalFirstAddressFromCIDR(cidr));
+        assertEquals("169.254.0.1/255.255.0.0", NetUtils.getLinkLocalAddressFromCIDR(cidr));
+        assertEquals("169.254.240.1/255.255.240.0", NetUtils.getLinkLocalAddressFromCIDR("169.254.240.0/20"));
+
+        String[] range = NetUtils.getLinkLocalIPRange("169.254.0.0/16");
+        assertEquals("169.254.0.2", range[0]);
+        assertEquals("169.254.255.254", range[1]);
+    }
+
+    @Test
+    public void testCidrNetmask() {
+        assertEquals("255.255.255.0", NetUtils.cidr2Netmask("192.168.0.0/24"));
+        assertEquals("255.255.0.0", NetUtils.cidr2Netmask("169.254.0.0/16"));
+        assertEquals("255.255.240.0", NetUtils.cidr2Netmask("169.254.240.0/20"));
+    }
 }