【问题标题】:Validate an IP Address (with Mask)验证 IP 地址(带掩码)
【发布时间】:2010-11-17 22:21:31
【问题描述】:

我有 IP 地址和掩码,例如 10.1.1.1/32。我想检查10.1.1.1 是否在该范围内。是否有可以执行此操作的库或实用程序,还是我需要自己编写一些东西?

【问题讨论】:

标签: java grails groovy


【解决方案1】:

首先,您需要将您的 IP 地址转换为平面 ints,这样会更容易使用:

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);

一旦你的 IP 地址是普通的ints,你就可以做一些位算术来执行检查:

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.
}

【讨论】:

  • 假设我以String ip = "10.1.1.1"; int mask = 24 开头,我如何获得0x0A010100?
  • 那么在基类库或一些推荐的第三方网络掩码处理包中真的不支持这个吗?
  • 对 IPv6 地址重写解决方案也很有用
  • 我用这个和 android 来获取掩码的网络地址。但是结果是错误的而不是 192.168.178.0 我得到了 0.178.168.192。您必须将 24,16,8,0 的顺序切换为 0,8,16,24
  • 我认为bits = 0('/0' 掩码)可能会损坏
【解决方案2】:
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;
        }
}

【讨论】:

    【解决方案3】:

    这是一个以几种常见方式获取子网描述的版本,包括 IPv6。

    基于此处发布的其他代码。 在 IPv4 地址上,它的运行速度可能比在裸 int 上执行二进制操作的方法慢。

    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 ) );
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      这只是带有open-source IPAddress Java library 的几行代码。免责声明:我是 IPAddress 库的项目经理。

      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);
      

      输出:

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

      【讨论】:

        【解决方案5】:

        我相信commons-ip-math 库做得很好。请注意,截至 2019 年 5 月,该库已有 2 年没有任何更新(可能是它已经非常成熟的库)。它可以在maven-central

        它支持使用 IPv4 和 IPv6 地址。他们的简短文档提供了有关如何检查地址是否在 IPv4 和 IPv6 的特定范围内的示例

        IPv4 范围检查示例:

                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);
        

        【讨论】:

          【解决方案6】:

          感谢 John Kugelman——我使用他的代码 sn-ps 创建了这个类。

          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;
            }
          
          }
          

          我确实必须在他的代码中为 int 转换添加一个掩码:

          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)));
          

          制作:

          ffffffc0
          c0c0c0c0
          

          在我的系统上:

          $> 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)
          $> 
          

          您可以从上面的类中删除 main 和 test 方法。它们改编自单元测试代码并在此处添加为简单起见。

          【讨论】:

            猜你喜欢
            • 2015-10-18
            • 1970-01-01
            • 1970-01-01
            • 2013-06-06
            • 2011-07-28
            • 1970-01-01
            • 1970-01-01
            • 2012-04-27
            • 2012-05-17
            相关资源
            最近更新 更多