如何在 Java 中将 IP 地址范围转换为 CIDR?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33443914/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:40:28  来源:igfitidea点击:

How to convert IP address range to CIDR in Java?

javaipcidr

提问by Dhaval Kotecha

I am trying to convert the IP address range into the CIDR notation in Java. Can someone provide an example of how this can be achieved?

我正在尝试将 IP 地址范围转换为 Java 中的 CIDR 表示法。有人可以提供一个如何实现这一目标的例子吗?

I used the SubnetUtils for converting the CIDR to IP address range, but I can't figure out the other way round.

我使用 SubnetUtils 将 CIDR 转换为 IP 地址范围,但我无法找出相反的方法。

For example: (using http://ip2cidr.com/)

例如:(使用http://ip2cidr.com/

Input 1:5.10.64.0
Input 2:5.10.127.255
Result:5.10.64.0/18

输入 1:5.10.64.0
输入 2:5.10.127.255
结果:5.10.64.0/18

Thanks, Dhaval

谢谢,达瓦尔

采纳答案by Sean F

The open-source IPAddress Java librarycan do this for you. Disclaimer: I am the project manager of the IPAddress library.

开源的ip地址Java库可以为你做到这一点。免责声明:我是 IPAddress 库的项目经理。

Here is sample code to do it:

这是执行此操作的示例代码:

static void toPrefixBlocks(String str1, String str2) {
    IPAddressString string1 = new IPAddressString(str1);
    IPAddressString string2 = new IPAddressString(str2);
    IPAddress one = string1.getAddress(), two = string2.getAddress();
    IPAddressSeqRange range = one.toSequentialRange(two);
    System.out.println("starting with range " + range);
    IPAddress blocks[] = range.spanWithPrefixBlocks();
    System.out.println("prefix blocks are " + Arrays.asList(blocks));
}

This is how you would use IPAddress to produce the range from the original CIDR string:

这是使用 IPAddress 从原始 CIDR 字符串生成范围的方式:

static String[] toRange(String str) {
    IPAddressString string = new IPAddressString(str);
    IPAddress addr = string.getAddress();
    System.out.println("starting with CIDR " + addr);
    IPAddress lower = addr.getLower(), upper = addr.getUpper();
    System.out.println("range is " + lower + " to " + upper);
    return new String[] {lower.toString(), upper.toString()};
}

For your example, we run this code:

对于您的示例,我们运行以下代码:

String strs[] = toRange("5.10.64.0/18");
System.out.println();
toPrefixBlocks(strs[0], strs[1]);

The output is:

输出是:

starting with CIDR 5.10.64.0/18
range is 5.10.64.0/18 to 5.10.127.255/18

starting with range 5.10.64.0 -> 5.10.127.255
prefix blocks are [5.10.64.0/18]

回答by u6378031

import java.util.ArrayList;
import java.util.List;

public class RangeToCidr {
    public static List<String> range2cidrlist( String startIp, String endIp ) {
        // check parameters
        if (startIp == null || startIp.length() < 8 ||
            endIp == null || endIp.length() < 8) return null;
        long start = ipToLong(startIp);
        long end = ipToLong(endIp);
        // check parameters
        if (start > end) return null;

        List<String> result = new ArrayList<String>();
        while (start <= end) {
            // identify the location of first 1's from lower bit to higher bit of start IP
            // e.g. 00000001.00000001.00000001.01101100, return 4 (100)
            long locOfFirstOne = start & (-start);
            int maxMask = 32 - (int) (Math.log(locOfFirstOne) / Math.log(2));

            // calculate how many IP addresses between the start and end
            // e.g. between 1.1.1.111 and 1.1.1.120, there are 10 IP address
            // 3 bits to represent 8 IPs, from 1.1.1.112 to 1.1.1.119 (119 - 112 + 1 = 8)
            double curRange = Math.log(end - start + 1) / Math.log(2);
            int maxDiff = 32 - (int) Math.floor(curRange);

            // why max?
            // if the maxDiff is larger than maxMask
            // which means the numbers of IPs from start to end is smaller than mask range
            // so we can't use as many as bits we want to mask the start IP to avoid exceed the end IP
            // Otherwise, if maxDiff is smaller than maxMask, which means number of IPs is larger than mask range
            // in this case we can use maxMask to mask as many as IPs from start we want.
            maxMask = Math.max(maxDiff, maxMask);

            // Add to results
            String ip = longToIP(start);
            result.add(ip + "/" + maxMask);
            // We have already included 2^(32 - maxMask) numbers of IP into result
            // So the next round start must add that number
            start += Math.pow(2, (32 - maxMask));
        }
        return result;
    }

    private static long ipToLong(String strIP) {
        String[] ipSegs = strIP.split("\.");
        long res = 0;
        for (int i = 0; i < 4; i++) {
            res += Long.valueOf(ipSegs[i]) << (8 * (3 - i));
        }
        return res;
    }

    private static String longToIP(long longIP) {
        StringBuffer sb = new StringBuffer();
        sb.append(longIP >>> 24).append(".")
          .append((longIP & 0x00FFFFFF) >>> 16).append(".")
          .append(String.valueOf((longIP & 0x0000FFFF) >>> 8)).append(".")
          .append(String.valueOf(longIP & 0x000000FF));

        return sb.toString();
    }
}

回答by Dhaval Kotecha

So, I was able to find the Java code here: In Java, given an IP Address range, return the minimum list of CIDR blocks that covers the range

所以,我可以在这里找到 Java 代码:在 Java 中,给定一个 IP 地址范围,返回覆盖该范围的 CIDR 块的最小列表

public class IP2CIDR {

    public static void main(String[] args) {
        System.out.println(range2cidrlist("5.104.109.160", "5.104.109.191"));
    }

    public static List<String> range2cidrlist( String startIp, String endIp ) {         
        long start = ipToLong(startIp);         
        long end = ipToLong(endIp);           

        ArrayList<String> pairs = new ArrayList<String>();         
        while ( end >= start ) {             
            byte maxsize = 32;             
            while ( maxsize > 0) {                 
                long mask = CIDR2MASK[ maxsize -1 ];                 
                long maskedBase = start & mask;                 

                if ( maskedBase != start ) {                     
                    break;                 
                }                 

                maxsize--;             
            }               
            double x = Math.log( end - start + 1) / Math.log( 2 );             
            byte maxdiff = (byte)( 32 - Math.floor( x ) );             
            if ( maxsize < maxdiff) {                 
                maxsize = maxdiff;             
            }             
            String ip = longToIP(start);             
            pairs.add( ip + "/" + maxsize);             
            start += Math.pow( 2, (32 - maxsize) );         
        }         
        return pairs;     
    }       

    public static final int[] CIDR2MASK = new int[] { 0x00000000, 0x80000000,             
        0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000,             
        0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000,             
        0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,             
        0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800,             
        0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0,             
        0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,             
        0xFFFFFFFF };       

    private static long ipToLong(String strIP) {         
        long[] ip = new long[4];         
        String[] ipSec = strIP.split("\.");         
        for (int k = 0; k < 4; k++) {             
            ip[k] = Long.valueOf(ipSec[k]);         
        }         

        return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];     
    }       

    private static String longToIP(long longIP) {         
        StringBuffer sb = new StringBuffer("");         
        sb.append(String.valueOf(longIP >>> 24));         
        sb.append(".");         
        sb.append(String.valueOf((longIP & 0x00FFFFFF) >>> 16));         
        sb.append(".");         
        sb.append(String.valueOf((longIP & 0x0000FFFF) >>> 8));         
        sb.append(".");         
        sb.append(String.valueOf(longIP & 0x000000FF));   

        return sb.toString();     
    }
}

Thanks everyone for your insights and help!

感谢大家的见解和帮助!

回答by Ron Maupin

In case you haven't figured it out from my comments:

如果你还没有从我的评论中弄清楚:

IP math must be done in binary. IP addresses and masks are unsigned integers (32 bits for IPv4, 128 bits for IPv6). All you need to know is an address and mask, and you can figure out everything else.

IP 数学必须以二进制形式完成。IP 地址和掩码是无符号整数(IPv4 为 32 位,IPv6 为 128 位)。你只需要知道一个地址和掩码,你就可以弄清楚其他一切。

This is algorithm for what you want to accomplish, and it applies to both IPv4 and IPv6.

这是您想要完成的算法,它适用于 IPv4 和 IPv6。

Based on your question, you are given the subnet (Input 1) and last address (Input 2).

根据您的问题,您将获得子网(输入 1)和最后一个地址(输入 2)。

  1. Subtract the unsigned integer of Input 1 from the unsigned integer of Input 2. The result is the inverse subnet mask. The inverse subnet mask must be 0, or the inverse subnet mask plus 1must be a power of 2, else you have an error in one of the inputs (STOP, INPUT ERROR).
  2. The NOTof the inverse mask (result of Step 1) is the subnet mask.
  3. If Input 1 ANDthe subnet mask does not equal Input 1, you have an error in one of the inputs (STOP, INPUT ERROR).
  4. The mask length (CIDR number) is the number of 1bits in the subnet mask. There are several ways to calculate the number of 1bits in a binary number, but if the subnet mask is the maximum integer (or the inverse mask is 0), then the mask length is 32(IPv4) or 128(IPv6). You can loop, counting the number of loops and shifting the subnet mask to the left until it equals 0, loop counting the number of loops and shifting the inverse mask to the right until it equals 0then adding 1to the total and subtracting the total from 32(IPv4) or 128(IPv6), or subtract the exponent of the power of 2of the total inverse mask plus 1from 32(IPv4) or 128(IPv6).
  5. At this point, you have the verified Input 1 (subnet), Input 2 (last address), and calculated the mask length (CIDR number).
  6. The final result will be <Input 1>/<Mask Length>.
  1. 从输入 2 的无符号整数中减去输入 1 的无符号整数。结果是反向子网掩码。反子网掩码必须是0,或者反子网掩码加1必须是 的幂2,否则输入之一(STOP, INPUT ERROR)有错误
  2. NOT反向掩膜(步骤1的结果)的是子网掩码。
  3. 如果输入 1AND的子网掩码不等于输入 1,则您的输入之一有错误(停止,输入错误)。
  4. 掩码长度(CIDR 编号)是1子网掩码中的位数。计算1一个二进制数的位数有多种方法,但如果子网掩码是最大整数(或反掩码为0),则掩码长度为32(IPv4)或128(IPv6)。您可以循环,计算循环次数并将子网掩码向左移动直到它等于0,循环计算循环次数并将逆掩码向右移动直到它等于0然后添加1到总数中并从32(IPv4) 中减去总数) 或128(IPv6),或从(IPv4) 或(IPv6) 中减去2总逆掩码的幂的指数。132128
  5. 此时,您已验证输入 1(子网)、输入 2(最后一个地址),并计算出掩码长度(CIDR 编号)。
  6. 最终结果将是<Input 1>/<Mask Length>

Your example:

你的例子:

Step 1 (5.10.127.255 - 5.10.64.0 = 0.0.64.127):

步骤 1 ( 5.10.127.255 - 5.10.64.0 = 0.0.64.127):

101000010100111111111111111 - 01000010100100000000000000 = 11111111111111

Step 2 (NOT 0.0.64.255 = 255.255.192.0is a power of two):

第 2 步(NOT 0.0.64.255 = 255.255.192.0是2的幂):

NOT 00000000000000000011111111111111 = 11111111111111111100000000000000

Step 3 (5.10.64.0 AND 255.255.192.0 = 5.10.64.0):

步骤 3 ( 5.10.64.0 AND 255.255.192.0 = 5.10.64.0):

01000010100100000000000000 AND 11111111111111111100000000000000 = 01000010100100000000000000

Step 4 (0.0.64.255 + 1 = 0.0.65.0 = 2^14, exponent of 2^14 = 14, 32 - 14 = 18):

第 4 步 ( 0.0.64.255 + 1 = 0.0.65.0 = 2^14, exponent of 2^14 = 14, 32 - 14 = 18):

00000000000000000011111111111111 + 1 = 00000000000000000100000000000000 = 2^14, exponent of 2^14 = 14, 32 - 14 = 18

Step 5 (Input 1 = 5.10.64.0, Input 2 = 5.10.127.255, Mask Length = 18)

第 5 步(输入 1 = 5.10.64.0,输入 2 = 5.10.127.255,掩码长度 = 18

Step 6 (Final Result = 5.10.64.0/18)

第 6 步(最终结果 = 5.10.64.0/18

回答by PaulQ

Something short and sweet in Python:

Python 中的一些简短而甜蜜的东西:

#!/usr/bin/env python
import ipaddress
import math

ip_from = '5.10.64.0'
ip_to = '5.10.127.255'
ip_from_long = int(ipaddress.ip_address(unicode(ip_from)))
ip_to_long = int(ipaddress.ip_address(unicode(ip_to)))
ip_range = ip_to_long - ip_from_long
ip_range +=1
# the clever line of code
cidr_range = math.log(4294967296/ip_range)/math.log(2)
# test for a zero/non-zero fractional part
if cidr_range % 1 == 0:
  # the output will be: 5.10.64.0/18
  print ip_from + '/' + str(int(cidr_range))
else:
  print "Error: Not an exact CIDR range - " + str(cidr_range)

回答by Xiaodong Ma

public static int log2(int i) {
    int count = 0;
    i >>= 1;
    while(i > 0) {
        count++;
        i >>= 1;
    }
    return count;
}

public static List<String> range2CIDR(String startIp, String endIp) {
    List<String> res = new ArrayList<>();
    try {
        int start = ipS2I(startIp);
        int end = ipS2I(endIp);
        while(start <= end) {
            int firstNonZero = start & -start;
            int maxMask = 32 - log2(firstNonZero);
            maxMask = Math.max(maxMask, 32 - log2(end - start + 1));
            res.add(ipI2S(start) + "/" + maxMask);
            start += 1 << (32 - maxMask);
        }
    }catch(Exception e) {
        return res;
    }

    return res;
}

public static int ipS2I(String ip) throws Exception {
    String[] sa = ip.split("\.");
    if (sa.length != 4) {
        throw new Exception("Bad ip address");
    }
    int res = 0;
    for (int i = 0; i < 4; i++) {
        int t = Integer.valueOf(sa[i]);
        if (t < 0 || t > 255) {
            throw new Exception("Bad ip address");
        }
        res += t << ((3-i) << 3);
    }
    return res;
}

public static String ipI2S(int ip) {
    StringBuilder sb = new StringBuilder();
    sb.append((ip>>24) & 0xFF).append(".").append((ip>>16)&0xFF).append(".").append((ip>>8) & 0xFF).append(".").append(ip & 0xFF);
    return sb.toString();
}