开发者

Validate an IP Address (with Mask)

开发者 https://www.devze.com 2023-01-25 09:30 出处:网络
I have ip addresses and a mask such as 10.1.1.1/32. I would like to check if 10.1.1.1 is inside that range. Is there a library or开发者_开发技巧 utility that would do this or do I need to write someth

I have ip addresses and a mask such as 10.1.1.1/32. I would like to check if 10.1.1.1 is inside that range. Is there a library or开发者_开发技巧 utility that would do this or do I need to write something myself?


First you'll want to convert your IP addresses into flat ints, which will be easier to work with:

String       s = "10.1.1.99";
Inet4Address a = (Inet4Address) InetAddress.getByName(s);
byte[]       b = a.getAddress();
int          i = ((b[0] & 0xFF) << 24) |
                 ((b[1] & 0xFF) << 16) |
                 ((b[2] & 0xFF) <<  8) |
                 ((b[3] & 0xFF) <<  0);

Once you have your IP addresses as plain ints you can do some bit arithmetic to perform the check:

int subnet = 0x0A010100;   // 10.1.1.0/24
int bits   = 24;
int ip     = 0x0A010163;   // 10.1.1.99

// Create bitmask to clear out irrelevant bits. For 10.1.1.0/24 this is
// 0xFFFFFF00 -- the first 24 bits are 1's, the last 8 are 0's.
//
//     -1        == 0xFFFFFFFF
//     32 - bits == 8
//     -1 << 8   == 0xFFFFFF00
mask = -1 << (32 - bits)

if ((subnet & mask) == (ip & mask)) {
    // IP address is in the subnet.
}


public static boolean netMatch(String addr, String addr1){ //addr is subnet address and addr1 is ip address. Function will return true, if addr1 is within addr(subnet)

        String[] parts = addr.split("/");
        String ip = parts[0];
        int prefix;

        if (parts.length < 2) {
            prefix = 0;
        } else {
            prefix = Integer.parseInt(parts[1]);
        }

        Inet4Address a =null;
        Inet4Address a1 =null;
        try {
            a = (Inet4Address) InetAddress.getByName(ip);
            a1 = (Inet4Address) InetAddress.getByName(addr1);
        } catch (UnknownHostException e){}

        byte[] b = a.getAddress();
        int ipInt = ((b[0] & 0xFF) << 24) |
                         ((b[1] & 0xFF) << 16) |
                         ((b[2] & 0xFF) << 8)  |
                         ((b[3] & 0xFF) << 0);

        byte[] b1 = a1.getAddress();
        int ipInt1 = ((b1[0] & 0xFF) << 24) |
                         ((b1[1] & 0xFF) << 16) |
                         ((b1[2] & 0xFF) << 8)  |
                         ((b1[3] & 0xFF) << 0);

        int mask = ~((1 << (32 - prefix)) - 1);

        if ((ipInt & mask) == (ipInt1 & mask)) {
            return true;
        }
        else {
            return false;
        }
}


Here is a version that takes subnet descriptions in several common ways, including IPv6.

Based on the other code posted here. On IPv4 addresses it might work slower than the approach of performing binary operations on bare int's.

package de.c3oe.tryanderror;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @author c3oe.de, based on snippets from Scott Plante, John Kugelmann
 */
public class Subnet
{
    final private int bytesSubnetCount;
    final private BigInteger bigMask;
    final private BigInteger bigSubnetMasked;

    /** For use via format "192.168.0.0/24" or "2001:db8:85a3:880:0:0:0:0/57" */
    public Subnet( final InetAddress subnetAddress, final int bits )
    {
        this.bytesSubnetCount = subnetAddress.getAddress().length; // 4 or 16
        this.bigMask = BigInteger.valueOf( -1 ).shiftLeft( this.bytesSubnetCount*8 - bits ); // mask = -1 << 32 - bits
        this.bigSubnetMasked = new BigInteger( subnetAddress.getAddress() ).and( this.bigMask );
    }

    /** For use via format "192.168.0.0/255.255.255.0" or single address */
    public Subnet( final InetAddress subnetAddress, final InetAddress mask )
    {
        this.bytesSubnetCount = subnetAddress.getAddress().length;
        this.bigMask = null == mask ? BigInteger.valueOf( -1 ) : new BigInteger( mask.getAddress() ); // no mask given case is handled here.
        this.bigSubnetMasked = new BigInteger( subnetAddress.getAddress() ).and( this.bigMask );
    }

    /**
     * Subnet factory method.
     * @param subnetMask format: "192.168.0.0/24" or "192.168.0.0/255.255.255.0"
     *      or single address or "2001:db8:85a3:880:0:0:0:0/57"
     * @return a new instance
     * @throws UnknownHostException thrown if unsupported subnet mask.
     */
    public static Subnet createInstance( final String subnetMask )
            throws UnknownHostException
    {
        final String[] stringArr = subnetMask.split("/");
        if ( 2 > stringArr.length )
            return new Subnet( InetAddress.getByName( stringArr[ 0 ] ), (InetAddress)null );
        else if ( stringArr[ 1 ].contains(".") || stringArr[ 1 ].contains(":") )
            return new Subnet( InetAddress.getByName( stringArr[ 0 ] ), InetAddress.getByName( stringArr[ 1 ] ) );
        else
            return new Subnet( InetAddress.getByName( stringArr[ 0 ] ), Integer.parseInt( stringArr[ 1 ] ) );
    }

    public boolean isInNet( final InetAddress address )
    {
        final byte[] bytesAddress = address.getAddress();
        if ( this.bytesSubnetCount != bytesAddress.length )
            return false;
        final BigInteger bigAddress = new BigInteger( bytesAddress );
        return  bigAddress.and( this.bigMask ).equals( this.bigSubnetMasked );
    }

    @Override
    final public boolean equals( Object obj )
    {
        if ( ! (obj instanceof Subnet) )
            return false;
        final Subnet other = (Subnet)obj;
        return  this.bigSubnetMasked.equals( other.bigSubnetMasked ) &&
                this.bigMask.equals( other.bigMask ) &&
                this.bytesSubnetCount == other.bytesSubnetCount;
    }

    @Override
    final public int hashCode()
    {
        return this.bytesSubnetCount;
    }

    @Override
    public String toString()
    {
        final StringBuilder buf = new StringBuilder();
        bigInteger2IpString( buf, this.bigSubnetMasked, this.bytesSubnetCount );
        buf.append( '/' );
        bigInteger2IpString( buf, this.bigMask, this.bytesSubnetCount );
        return buf.toString();
    }

    static private void bigInteger2IpString( final StringBuilder buf, final BigInteger bigInteger, final int displayBytes )
    {
        final boolean isIPv4 = 4 == displayBytes;
        byte[] bytes = bigInteger.toByteArray();
        int diffLen = displayBytes - bytes.length;
        final byte fillByte = 0 > (int)bytes[ 0 ] ? (byte)0xFF : (byte)0x00;

        int integer;
        for ( int i = 0; i < displayBytes; i++ )
        {
            if ( 0 < i && ! isIPv4 && i % 2 == 0 )
                buf.append( ':' );
            else if ( 0 < i && isIPv4 )
                buf.append( '.' );
            integer = 0xFF & (i < diffLen ? fillByte : bytes[ i - diffLen ]);
            if ( ! isIPv4 && 0x10 > integer )
                buf.append( '0' );
            buf.append( isIPv4 ? integer : Integer.toHexString( integer ) );
        }
    }
}


This is just a few lines of code with the open-source IPAddress Java library. Disclaimer: I am the project manager of the IPAddress library.

String subnetStr = "10.1.1.1/24";
String addrStr = "10.1.1.1";
IPAddress subnetAddress = new IPAddressString(subnetStr).getAddress();
IPAddress subnet = subnetAddress.toPrefixBlock();
IPAddress testAddress = new IPAddressString(addrStr).getAddress();
boolean result = subnet.contains(testAddress);
System.out.println(subnetAddress + " is in subnet " + subnet + " and " +
            (result ? "contains" : "does not contain") + " address " + testAddress);

Output:

10.1.1.1/24 is in subnet 10.1.1.0/24 and contains address 10.1.1.1


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 for 2 years (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 = "10.1.1.1";
        Ipv4 ipv41 = Ipv4.parse(input1);

        // Using CIDR notation to specify the networkID and netmask
        Ipv4Range range = Ipv4Range.parse("10.1.1.1/32");
        boolean result = range.contains(ipv41);
        System.out.println(result); //true

        String input2 = "10.1.1.1";
        Ipv4 ipv42 = Ipv4.parse(input2);

        // Specifying the range with a start and end.
        Ipv4 start = Ipv4.of("10.1.1.1");
        Ipv4 end = Ipv4.of("10.1.1.1");
        range = Ipv4Range.from(start).to(end);

        result = range.contains(ipv42); //true
        System.out.println(result);


Thanks to John Kugelman--I used his code snippets to create this class.

package bs;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * Represents an IP range based on an address/mask.
 * @author Scott Plante, using code snippets by John Kugelman.
 */
public class IPMask
{
  public static void main(String args[]) 
      throws UnknownHostException
  {
    IPMask ipmask;

    ipmask = IPMask.getIPMask("192.168.20.32/24");
    System.out.println("Checking "+ipmask+"...");

    test(ipmask, "192.168.20.31 ", true);
    test(ipmask, "192.168.20.32 ", true);
    test(ipmask, "192.168.20.33 ", true);
    test(ipmask, "192.168.20.34 ", true);
    test(ipmask, "192.168.20.35 ", true);
    test(ipmask, "192.168.20.36 ", true);
    test(ipmask, "192.168.20.254", true);
    test(ipmask, "192.168.20.157", true);
    test(ipmask, "192.168.21.1  ", false);
    test(ipmask, "192.168.19.255", false);
    test(ipmask, "192.168.24.1  ", false);

    ipmask = IPMask.getIPMask("192.168.20.32/31");
    System.out.println("Checking "+ipmask+"...");

    test(ipmask, "192.168.20.31 ", false);
    test(ipmask, "192.168.20.32 ", true);
    test(ipmask, "192.168.20.33 ", true);
    test(ipmask, "192.168.20.34 ", false);
    test(ipmask, "192.168.20.35 ", false);
    test(ipmask, "192.168.20.36 ", false);
    test(ipmask, "192.168.20.254", false);
    test(ipmask, "192.168.20.157", false);
    test(ipmask, "192.168.21.1  ", false);
    test(ipmask, "192.168.19.255", false);
    test(ipmask, "192.168.24.1  ", false);

    ipmask = IPMask.getIPMask("192.168.20.32/23");
    System.out.println("Checking "+ipmask+"...");

    test(ipmask, "192.168.20.31 ", true);
    test(ipmask, "192.168.20.32 ", true);
    test(ipmask, "192.168.20.33 ", true);
    test(ipmask, "192.168.20.254", true);
    test(ipmask, "192.168.21.254", true);
    test(ipmask, "192.168.19.255", false);
    test(ipmask, "192.168.24.1  ", false);

  }

  public static void test(IPMask ipmask, String addr, boolean expect) 
      throws UnknownHostException
  {
    boolean got = ipmask.matches(addr);
    System.out.println(addr + "\t(" + expect + ") ?\t"+got
        + "\t" + (got==expect?"":"!!!!!!!!"));
  }

  private Inet4Address i4addr;
  private byte maskCtr;

  private int addrInt;
  private int maskInt;

  public IPMask(Inet4Address i4addr, byte mask)
  {
    this.i4addr = i4addr;
    this.maskCtr = mask;

    this.addrInt = addrToInt(i4addr);
    this.maskInt = ~((1 << (32 - maskCtr)) - 1);
  }

  /** IPMask factory method. 
   * 
   * @param addrSlashMask IP/Mask String in format "nnn.nnn.nnn.nnn/mask". If 
   *    the "/mask" is omitted, "/32" (just the single address) is assumed.
   * @return a new IPMask
   * @throws UnknownHostException if address part cannot be parsed by 
   *    InetAddress
   */
  public static IPMask getIPMask(String addrSlashMask) 
      throws UnknownHostException
  {
    int pos = addrSlashMask.indexOf('/');
    String addr;
    byte maskCtr;
    if (pos==-1)
    {
      addr = addrSlashMask;
      maskCtr = 32;
    }
    else
    { 
      addr = addrSlashMask.substring(0, pos);
      maskCtr = Byte.parseByte(addrSlashMask.substring(pos + 1));
    }
    return new IPMask((Inet4Address) InetAddress.getByName(addr), maskCtr);
  }

 /** Test given IPv4 address against this IPMask object.
   * 
   * @param testAddr address to check.
   * @return true if address is in the IP Mask range, false if not.
   */  
  public boolean matches(Inet4Address testAddr)
  {
    int testAddrInt = addrToInt(testAddr);   
    return ((addrInt & maskInt) == (testAddrInt & maskInt));
  }

/** Convenience method that converts String host to IPv4 address.
   * 
   * @param addr IP address to match in nnn.nnn.nnn.nnn format or hostname.
   * @return true if address is in the IP Mask range, false if not.
   * @throws UnknownHostException if the string cannot be decoded.
   */
  public boolean matches(String addr) 
      throws UnknownHostException
  {
    return matches((Inet4Address)InetAddress.getByName(addr));
  }

/** Converts IPv4 address to integer representation.
   */
  private static int addrToInt(Inet4Address i4addr)
  {
    byte[] ba = i4addr.getAddress();  
    return (ba[0]       << 24) 
        | ((ba[1]&0xFF) << 16) 
        | ((ba[2]&0xFF) << 8) 
        |  (ba[3]&0xFF);
  }

  @Override
  public String toString()
  {
    return "IPMask(" + i4addr.getHostAddress() + "/" + maskCtr + ")";
  }

  @Override
  public boolean equals(Object obj)
  {
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final IPMask that = (IPMask) obj;    
    return (this.addrInt == that.addrInt && this.maskInt == that.maskInt);
  }

  @Override
  public int hashCode()
  {
    return this.maskInt + this.addrInt;
  }

}

I did have to add a mask to the int conversion in his code:

Inet4Address a = (Inet4Address) InetAddress.getByName("192.192.192.192");
byte[]       b = a.getAddress();
int          i = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3] << 0);
System.out.println(Integer.toHexString(i));
System.out.println(Integer.toHexString(addrToInt(a)));

Produced:

ffffffc0
c0c0c0c0

On my system:

$> uname -a
Linux guin 2.6.37.6-0.5-desktop #1 SMP PREEMPT 2011-04-25 21:48:33 +0200 x86_64 x86_64 x86_64 GNU/Linux
$> java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)
$> 

You can remove the main and test methods from the above class. They're adapted from the unit test code and added here for simplicity.

0

精彩评论

暂无评论...
验证码 换一张
取 消