I need to determine if given IP address is from some special network in order to authenticate automatically.
here is an Version that works with IPv4 and IPv6 one with Prefix and one with Network Mask.
/**
* Check if IP is within an Subnet defined by Network Address and Network Mask
* @param ip
* @param net
* @param mask
* @return
*/
public static final boolean isIpInSubnet(final String ip, final String net, final int prefix) {
try {
final byte[] ipBin = java.net.InetAddress.getByName(ip ).getAddress();
final byte[] netBin = java.net.InetAddress.getByName(net ).getAddress();
if(ipBin.length != netBin.length ) return false;
int p = prefix;
int i = 0;
while(p>=8) { if(ipBin[i] != netBin[i] ) return false; ++i; p-=8; }
final int m = (65280 >> p) & 255;
if((ipBin[i] & m) != (netBin[i]&m) ) return false;
return true;
} catch(final Throwable t) {
return false;
}
}
/**
* Check if IP is within an Subnet defined by Network Address and Network Mask
* @param ip
* @param net
* @param mask
* @return
*/
public static final boolean isIpInSubnet(final String ip, final String net, final String mask) {
try {
final byte[] ipBin = java.net.InetAddress.getByName(ip ).getAddress();
final byte[] netBin = java.net.InetAddress.getByName(net ).getAddress();
final byte[] maskBin = java.net.InetAddress.getByName(mask).getAddress();
if(ipBin.length != netBin.length ) return false;
if(netBin.length != maskBin.length) return false;
for(int i = 0; i < ipBin.length; ++i) if((ipBin[i] & maskBin[i]) != (netBin[i] & maskBin[i])) return false;
return true;
} catch(final Throwable t) {
return false;
}
}
You can also try
boolean inSubnet = (ip & netmask) == (subnet & netmask);
or shorter
boolean inSubnet = (ip ^ subnet) & netmask == 0;
To check An IP in a subnet, I used isInRange method in SubnetUtils class. But this method have a bug that if your subnet was X, every IP address that lower than X, isInRange return true. For example if your subnet was 10.10.30.0/24 and you want to check 10.10.20.5, this method return true. To deal with this bug I used below code.
public static void main(String[] args){
String list = "10.10.20.0/24";
String IP1 = "10.10.20.5";
String IP2 = "10.10.30.5";
SubnetUtils subnet = new SubnetUtils(list);
SubnetUtils.SubnetInfo subnetInfo = subnet.getInfo();
if(MyisInRange(subnetInfo , IP1) == true)
System.out.println("True");
else
System.out.println("False");
if(MyisInRange(subnetInfo , IP2) == true)
System.out.println("True");
else
System.out.println("False");
}
private boolean MyisInRange(SubnetUtils.SubnetInfo info, String Addr )
{
int address = info.asInteger( Addr );
int low = info.asInteger( info.getLowAddress() );
int high = info.asInteger( info.getHighAddress() );
return low <= address && address <= high;
}
The open-source IPAddress Java library will do this in a polymorphic manner for both IPv4 and IPv6 and handles subnets. Disclaimer: I am the project manager of that library.
Example code:
contains("10.10.20.0/30", "10.10.20.3");
contains("10.10.20.0/30", "10.10.20.5");
contains("1::/64", "1::1");
contains("1::/64", "2::1");
contains("1::3-4:5-6", "1::4:5");
contains("1-2::/64", "2::");
contains("bla", "foo");
static void contains(String network, String address) {
IPAddressString one = new IPAddressString(network);
IPAddressString two = new IPAddressString(address);
System.out.println(one + " contains " + two + " " + one.contains(two));
}
Output:
10.10.20.0/30 contains 10.10.20.3 true
10.10.20.0/30 contains 10.10.20.5 false
1::/64 contains 1::1 true
1::/64 contains 2::1 false
1::3-4:5-6 contains 1::4:5 true
1-2::/64 contains 2:: true
bla contains foo false
Use spring-security-web's IpAddressMatcher. Unlike Apache Commons Net, it supports both ipv4 and ipv6.
import org.springframework.security.web.util.matcher.IpAddressMatcher;
...
private void checkIpMatch() {
matches("192.168.2.1", "192.168.2.1"); // true
matches("192.168.2.1", "192.168.2.0/32"); // false
matches("192.168.2.5", "192.168.2.0/24"); // true
matches("92.168.2.1", "fe80:0:0:0:0:0:c0a8:1/120"); // false
matches("fe80:0:0:0:0:0:c0a8:11", "fe80:0:0:0:0:0:c0a8:1/120"); // true
matches("fe80:0:0:0:0:0:c0a8:11", "fe80:0:0:0:0:0:c0a8:1/128"); // false
matches("fe80:0:0:0:0:0:c0a8:11", "192.168.2.0/32"); // false
}
private boolean matches(String ip, String subnet) {
IpAddressMatcher ipAddressMatcher = new IpAddressMatcher(subnet);
return ipAddressMatcher.matches(ip);
}
The code in previous part works perfectly fine but it needs spring-security-web
to be included.
If you are not willing to include Spring framework in your project, you may use this class which is a slightly modified version of the original class from Spring, so that it has no non-JRE dependencies.
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Matches a request based on IP Address or subnet mask matching against the remote
* address.
* <p>
* Both IPv6 and IPv4 addresses are supported, but a matcher which is configured with an
* IPv4 address will never match a request which returns an IPv6 address, and vice-versa.
*
* @author Luke Taylor
* @since 3.0.2
*
* Slightly modified by omidzk to have zero dependency to any frameworks other than the JRE.
*/
public final class IpAddressMatcher {
private final int nMaskBits;
private final InetAddress requiredAddress;
/**
* Takes a specific IP address or a range specified using the IP/Netmask (e.g.
* 192.168.1.0/24 or 202.24.0.0/14).
*
* @param ipAddress the address or range of addresses from which the request must
* come.
*/
public IpAddressMatcher(String ipAddress) {
if (ipAddress.indexOf('/') > 0) {
String[] addressAndMask = ipAddress.split("/");
ipAddress = addressAndMask[0];
nMaskBits = Integer.parseInt(addressAndMask[1]);
}
else {
nMaskBits = -1;
}
requiredAddress = parseAddress(ipAddress);
assert (requiredAddress.getAddress().length * 8 >= nMaskBits) :
String.format("IP address %s is too short for bitmask of length %d",
ipAddress, nMaskBits);
}
public boolean matches(String address) {
InetAddress remoteAddress = parseAddress(address);
if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
return false;
}
if (nMaskBits < 0) {
return remoteAddress.equals(requiredAddress);
}
byte[] remAddr = remoteAddress.getAddress();
byte[] reqAddr = requiredAddress.getAddress();
int nMaskFullBytes = nMaskBits / 8;
byte finalByte = (byte) (0xFF00 >> (nMaskBits & 0x07));
// System.out.println("Mask is " + new sun.misc.HexDumpEncoder().encode(mask));
for (int i = 0; i < nMaskFullBytes; i++) {
if (remAddr[i] != reqAddr[i]) {
return false;
}
}
if (finalByte != 0) {
return (remAddr[nMaskFullBytes] & finalByte) == (reqAddr[nMaskFullBytes] & finalByte);
}
return true;
}
private InetAddress parseAddress(String address) {
try {
return InetAddress.getByName(address);
}
catch (UnknownHostException e) {
throw new IllegalArgumentException("Failed to parse address" + address, e);
}
}
}
NOTICE: Notice that for using this option, it's your responsibility to carefully examine the license to make sure by using this code, you are not in violation of any terms mandated by the aforementioned license. (Of course publishing this code to Stackoverflow.com by me is not a violation.)
I know this is very old question, but I stumbled upon this when I was looking to solve the same problem.
There is commons-ip-math library that I believe does a very good job. Please note that as of May 2019, there hasn't been any updates to the library (Could be that its already very mature library). Its available on maven-central
It supports working with both IPv4 and IPv6 addresses. Their brief documentation has examples on how you can check if an address is in a specific range for IPv4 and IPv6
Example for IPv4 range checking:
String input1 = "192.168.1.0";
Ipv4 ipv41 = Ipv4.parse(input1);
// Using CIDR notation to specify the networkID and netmask
Ipv4Range range = Ipv4Range.parse("192.168.0.0/24");
boolean result = range.contains(ipv41);
System.out.println(result); //false
String input2 = "192.168.0.251";
Ipv4 ipv42 = Ipv4.parse(input2);
// Specifying the range with a start and end.
Ipv4 start = Ipv4.of("192.168.0.0");
Ipv4 end = Ipv4.of("192.168.0.255");
range = Ipv4Range.from(start).to(end);
result = range.contains(ipv42); //true
System.out.println(result);