mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-01-10 13:21:53 +01:00
Extend NetUtils for network range scanning (#4375)
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
parent
d012d36bba
commit
d9e5df0cd9
@ -19,6 +19,7 @@ import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -34,6 +35,7 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@ -55,6 +57,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Stefan Triller - Converted to OSGi service with primary ipv4 conf
|
||||
* @author Gary Tse - Network address change listener
|
||||
* @author Tim Roberts - Added primary address change to network address change listener
|
||||
* @author Leo Siepel - Added methods to improve support for network scanning
|
||||
*/
|
||||
@Component(configurationPid = "org.openhab.network", property = { "service.pid=org.openhab.network",
|
||||
"service.config.description.uri=system:network", "service.config.label=Network Settings",
|
||||
@ -386,7 +389,7 @@ public class NetUtil implements NetworkAddressService {
|
||||
/**
|
||||
* Get the network address a specific ip address is in
|
||||
*
|
||||
* @param ipAddressString ipv4 address of the device (i.e. 192.168.5.1)
|
||||
* @param ipAddressString IPv4 address of the device (i.e. 192.168.5.1)
|
||||
* @param netMask netmask in bits (i.e. 24)
|
||||
* @return network a device is in (i.e. 192.168.5.0)
|
||||
* @throws IllegalArgumentException if parameters are wrong
|
||||
@ -422,7 +425,7 @@ public class NetUtil implements NetworkAddressService {
|
||||
/**
|
||||
* Get the network broadcast address of the subnet a specific ip address is in
|
||||
*
|
||||
* @param ipAddressString ipv4 address of the device (i.e. 192.168.5.1)
|
||||
* @param ipAddressString IPv4 address of the device (i.e. 192.168.5.1)
|
||||
* @param prefix network prefix in bits (i.e. 24)
|
||||
* @return network broadcast address of the network the device is in (i.e. 192.168.5.255)
|
||||
* @throws IllegalArgumentException if parameters are wrong
|
||||
@ -595,4 +598,92 @@ public class NetUtil implements NetworkAddressService {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For all network interfaces (except loopback) all IPv4 addresses are returned.
|
||||
* This list can for example, be used to scan the network for available devices.
|
||||
*
|
||||
* @return A full list of IP {@link InetAddress} (except network and broadcast)
|
||||
*/
|
||||
public static List<InetAddress> getFullRangeOfAddressesToScan() {
|
||||
List<InetAddress> addressesToScan = List.of();
|
||||
List<CidrAddress> ipV4InterfaceAddresses = NetUtil.getAllInterfaceAddresses().stream()
|
||||
.filter(a -> a.getAddress() instanceof Inet4Address).collect(Collectors.toList());
|
||||
|
||||
for (CidrAddress i : ipV4InterfaceAddresses) {
|
||||
addressesToScan.addAll(getAddressesRangeByCidrAddress(i, i.getPrefix()));
|
||||
}
|
||||
return addressesToScan;
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given {@link CidrAddress} all IPv4 addresses are returned.
|
||||
* This list can, for example, be used to scan the network for available devices.
|
||||
*
|
||||
* @param iFaceAddress The {@link CidrAddress} of the network interface
|
||||
* @param maxAllowedPrefixLength Control the maximum allowed prefix length of the network (e.g. 24 for class C).
|
||||
* iFaceAddress's with a larger prefix are ignored and return an empty result.
|
||||
* @return A full list of IP {@link InetAddress} (except network and broadcast)
|
||||
*/
|
||||
public static List<InetAddress> getAddressesRangeByCidrAddress(CidrAddress iFaceAddress,
|
||||
int maxAllowedPrefixLength) {
|
||||
if (!(iFaceAddress.getAddress() instanceof Inet4Address) || iFaceAddress.getPrefix() < maxAllowedPrefixLength) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<byte[]> addresses = getAddressesInSubnet(iFaceAddress.getAddress().getAddress(), iFaceAddress.getPrefix());
|
||||
if (addresses.size() > 2) {
|
||||
addresses.remove(0); // remove network address
|
||||
addresses.remove(addresses.size() - 1); // remove broadcast address
|
||||
}
|
||||
|
||||
return addresses.stream().map(m -> {
|
||||
try {
|
||||
return InetAddress.getByAddress(m);
|
||||
} catch (UnknownHostException e) {
|
||||
return null;
|
||||
}
|
||||
}).filter(f -> f != null).sorted((a, b) -> {
|
||||
byte[] aOct = a.getAddress();
|
||||
byte[] bOct = b.getAddress();
|
||||
int r = 0;
|
||||
for (int i = 0; i < aOct.length && i < bOct.length; i++) {
|
||||
r = Integer.compare(aOct[i] & 0xff, bOct[i] & 0xff);
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate each IP address within a subnet
|
||||
*
|
||||
* @param address IPv4 address in byte array form (i.e. 127.0.0.1 = 01111111 00000000 00000000 00000001)
|
||||
* @param maskLength Network mask length (i.e. the number after the forward-slash, '/', in CIDR notation)
|
||||
* @return A list of all possible IP addresses in byte array form
|
||||
*/
|
||||
private static List<byte[]> getAddressesInSubnet(byte[] address, int maskLength) {
|
||||
byte[] lowestAddress = address.clone();
|
||||
for (int bit = maskLength; bit < 32; bit++) {
|
||||
lowestAddress[bit / 8] &= ~(1 << (bit % 8));
|
||||
}
|
||||
int lowestAddressAsLong = ByteBuffer.wrap(lowestAddress).getInt(); // big-endian by default
|
||||
|
||||
byte[] highestAddress = address.clone();
|
||||
for (int bit = maskLength; bit < 32; bit++) {
|
||||
highestAddress[bit / 8] |= ~(1 << (bit % 8));
|
||||
}
|
||||
int highestAddressAsLong = ByteBuffer.wrap(highestAddress).getInt();
|
||||
|
||||
List<byte[]> addresses = new ArrayList<byte[]>();
|
||||
for (int i = lowestAddressAsLong; i <= highestAddressAsLong; i++) {
|
||||
ByteBuffer dbuf = ByteBuffer.allocate(4);
|
||||
dbuf.putInt(i);
|
||||
addresses.add(dbuf.array());
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,11 @@ package org.openhab.core.net;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -116,4 +121,35 @@ public class NetUtilTest {
|
||||
assertThat(iae.getMessage(), is("IP 'SOME_TEXT' is not a valid IPv4 address"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkValidRangeCountAndSort() throws UnknownHostException {
|
||||
InetAddress testableAddress = InetAddress.getByName("192.168.1.4");
|
||||
List<InetAddress> addresses = NetUtil
|
||||
.getAddressesRangeByCidrAddress(new CidrAddress(testableAddress, (short) 24), 24);
|
||||
|
||||
assertEquals(254, addresses.size());
|
||||
assertEquals("192.168.1.1", addresses.get(0).getHostAddress());
|
||||
assertEquals("192.168.1.254", addresses.get(253).getHostAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkValidLargeRangeCountAndSort() throws UnknownHostException {
|
||||
InetAddress testableAddress = InetAddress.getByName("127.0.1.12");
|
||||
List<InetAddress> addresses = NetUtil
|
||||
.getAddressesRangeByCidrAddress(new CidrAddress(testableAddress, (short) 16), 16);
|
||||
|
||||
assertEquals(65534, addresses.size());
|
||||
assertEquals("127.0.0.1", addresses.get(0).getHostAddress());
|
||||
assertEquals("127.0.255.254", addresses.get(65533).getHostAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkNotAllowedPrefixLength() throws UnknownHostException {
|
||||
InetAddress testableAddress = InetAddress.getByName("192.168.1.0");
|
||||
List<InetAddress> addresses = NetUtil
|
||||
.getAddressesRangeByCidrAddress(new CidrAddress(testableAddress, (short) 16), 24);
|
||||
|
||||
assertEquals(0, addresses.size());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user