Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we'll discuss how to find whether an IP address is in a given range or not using Java. For this problem, we'll consider all the given IP addresses are valid IPv4 (Internet Protocol Version 4) and IPv6 (Internet Protocol Version 6) addresses throughout the article.

2. Introduction to the Problem

Given an input IP address along with the two other IP addresses as the range (from and to). We should be able to determine whether the input IP address is in the given range or not.

For Example:

  • Input = 192.220.3.0, Range between 192.210.0.0 and 192.255.0.0
    Output = true
  • Input = 192.200.0.0, Range between 192.210.0.0 and 192.255.0.0
    Output = false

Now, let's look at the different ways to check the given IP address is in range or not, using various Java libraries.

3. IPAddress Library

The IPAddress library, written by Sean C Foley, supports handling both IPv4 and IPv6 addresses for a wide range of use cases. It is important to note that this library requires at least Java 8 to work.

Setting up this library is straightforward. We need to add the ipaddress dependency to our pom.xml:

<dependency>
    <groupId>com.github.seancfoley</groupId>
    <artifactId>ipaddress</artifactId>
    <version>5.3.3</version>
</dependency>

It provides the following Java classes needed to solve our problem:

  • IPAddress, to hold the IP address as a Java instance
  • IPAddressString, to construct the IPAddress instance from the given IP as a string
  • IPAddressSeqRange, to represent an arbitrary range of IP addresses

Now, let's look at the code for finding whether an IP address is in the given range by using the above classes:

public static boolean checkIPIsInGivenRange (String inputIP, String rangeStartIP, String rangeEndIP) 
  throws AddressStringException {
    IPAddress startIPAddress = new IPAddressString(rangeStartIP).getAddress();
    IPAddress endIPAddress = new IPAddressString(rangeEndIP).getAddress();
    IPAddressSeqRange ipRange = startIPAddress.toSequentialRange(endIPAddress);
    IPAddress inputIPAddress = new IPAddressString(inputIP).toAddress();

    return ipRange.contains(inputIPAddress);
}

The above code works for both IPv4 and IPv6 addresses. IPAddressString parameterized constructor takes an IP as a string to construct IPAddress instance. IPAddressString instance can be converted to IPAddress by using any of the following two methods:

  • toAddress()
  • getAddress()

The getAddress() method assumes the given IP is valid, but the toAddress() method validates the input once and throws AddressStringException if it is invalid. IPAddress class provides a toSequentialRange method that constructs IPAddressSeqRange instance using the beginning and ending IP range.

Let's consider few unit cases which calls checkIPIsInGivenRange with IPv4 and IPv6 addresses:

@Test
void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange(
      "2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

@Test
void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange(
      "2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

4. Commons IP Math

Commons IP Math library provides classes for representing IPv4 and IPv6 addresses and ranges. It provides APIs for dealing with the most common operations, and in addition, it gives comparators and other utilities for working with IP ranges.

We need to add the commons-ip-math dependency to our pom.xml:

<dependency>
    <groupId>com.github.jgonian</groupId>
    <artifactId>commons-ip-math</artifactId>
    <version>1.32</version>
</dependency>

4.1. For IPv4

The library provides Ipv4 and Ipv4Range classes for holding a single IP address and a range of addresses as instances, respectively. Now, let's glance at the code sample which makes use of the aforementioned classes:

public static boolean checkIPv4IsInRange (String inputIP, String rangeStartIP, String rangeEndIP) {
    Ipv4 startIPAddress = Ipv4.of(rangeStartIP);
    Ipv4 endIPAddress = Ipv4.of(rangeEndIP);
    Ipv4Range ipRange = Ipv4Range.from(startIPAddress).to(endIPAddress);
    Ipv4 inputIPAddress = Ipv4.of(inputIP);
    return ipRange.contains(inputIPAddress);
}

Ipv4 class provides a static method of() that takes the IP string to construct an Ipv4 instance. Ipv4Range class uses the builder design pattern to create its instance by using from() and to() methods to specify the range. Further, it provides the contains the () function to check for an IP address present in the specified range or not.

Now let's run some tests against our function:

@Test
void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
}

4.2. For IPv6

For IP version 6, the library provides the same classes and functions with a change in the version number from 4 → 6. The classes for version 6 are Ipv6 and Ipv6Range. 

Let's peek at the code example for IP version 6 by utilizing the aforementioned classes:

public static boolean checkIPv6IsInRange (String inputIP, String rangeStartIP, String rangeEndIP) {
    Ipv6 startIPAddress = Ipv6.of(rangeStartIP);
    Ipv6 endIPAddress = Ipv6.of(rangeEndIP);
    Ipv6Range ipRange = Ipv6Range.from(startIPAddress).to(endIPAddress);
    Ipv6 inputIPAddress = Ipv6.of(inputIP);
    return ipRange.contains(inputIPAddress);
}

Now let's run the unit tests to check our code:

@Test
void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRange(
      "2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

@Test
void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRange(
      "2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

5. Using Java's InetAddress Class for IPv4

IPv4 address is a sequence of four 1-byte values. Hence, it can be converted to a 32-bit integer. We can check if it falls under the given range.

Java's InetAddress class represents an IP address and provides methods to get the IP for any given hostnames. An instance of InetAddress represents the IP address with its corresponding hostname.

Here's the Java code to convert an IPv4 address into a long integer:

long ipToLongInt (InetAddress ipAddress) {
    long resultIP = 0;
    byte[] ipAddressOctets = ipAddress.getAddress();

    for (byte octet : ipAddressOctets) {
        resultIP <<= 8;
        resultIP |= octet & 0xFF;
    }
    return resultIP;
}

By using the above method, let's check for IP is in the range:

public static boolean checkIPv4IsInRangeByConvertingToInt (String inputIP, String rangeStartIP, String rangeEndIP) 
  throws UnknownHostException {
    long startIPAddress = ipToLongInt(InetAddress.getByName(rangeStartIP));
    long endIPAddress = ipToLongInt(InetAddress.getByName(rangeEndIP));
    long inputIPAddress = ipToLongInt(InetAddress.getByName(inputIP));

    return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
}

The getByName() method in InetAddress class takes either domain name or IP address as input and throws UnknownHostException if it's invalid. Let's check our code by running unit tests:

@Test
void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.220.3.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.200.0.0", "192.210.0.0", "192.255.0.0"));
}

The above logic of converting an IP address to an integer also applies to IPv6, but it's a 128-bit integer. Java language supports a maximum of 64-bit (long integer) in primitive data types. If we have to apply the above logic for version 6, we need to use either two long integers or BigInteger class for computations. But it would be a tedious process and also involves complex calculations.

6. Java IPv6 Library

Java IPv6 library is written especially for IPv6 support in Java and to perform related operations on it. This library internally uses two long integers to store the IPv6 address. And it requires at least Java 6 to work.

We need the java-ipv6 dependency to be added to our pom.xml:

<dependency>
    <groupId>com.googlecode.java-ipv6</groupId>
    <artifactId>java-ipv6</artifactId>
    <version>0.17</version>
</dependency>

The library provides various classes to operate with IPv6 addresses. Here are the two of them which help us to solve our problem:

  • IPv6Address, for expressing IPv6 as a Java instance
  • IPv6AddressRange, for representing a continuous range of consecutive IPv6 addresses

Let's look at the code snippet that uses the above classes to check IP is in the given range:

public static boolean checkIPv6IsInRangeByIPv6library (String inputIP, String rangeStartIP, String rangeEndIP) {
    IPv6Address startIPAddress = IPv6Address.fromString(rangeStartIP);
    IPv6Address endIPAddress = IPv6Address.fromString(rangeEndIP);
    IPv6AddressRange ipRange = IPv6AddressRange.fromFirstAndLast(startIPAddress, endIPAddress);
    IPv6Address inputIPAddress = IPv6Address.fromString(inputIP);
    return ipRange.contains(inputIPAddress);
}

IPv6Address class gives us various static functions to construct its instance:

  • fromString
  • fromInetAddress
  • fromBigInteger
  • fromByteArray
  • fromLongs

All the above methods are self-explanatory, which helps us to create an IPv6Address instance. IPv6AddressRange has a method named fromFirstAndLast() that takes two IP addresses as input. In addition, it provides a contains() method that takes an IPv6Address as a parameter and determines if it is present in the specified range or not.

By calling the above method we defined, let's pass few sample inputs in our tests:

@Test
void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library(
      "fe80::226:2dff:fefa:dcba",
      "fe80::226:2dff:fefa:cd1f",
      "fe80::226:2dff:fefa:ffff"
    ));
}

@Test
void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library(
      "2002:db8:85a3::8a03:a:b",
      "2001:db8:85a3::8a00:ff:ffff",
      "2001:db8:85a3::8a2e:370:7334"
    ));
}

7. Conclusion

In this article, we examined how we can determine whether the given IP address (both v4 and v6) is in the specified range or not. With the help of various libraries, we analyzed checking the IP address presence without any complex logic and computations.

As always, the code snippets of this article can be found over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!