簡體   English   中英

如何在 Java 中將 IP 地址范圍轉換為 CIDR?

[英]How to convert IP address range to CIDR in Java?

我正在嘗試將 IP 地址范圍轉換為 Java 中的 CIDR 表示法。 有人可以提供一個如何實現這一目標的例子嗎?

我使用 SubnetUtils 將 CIDR 轉換為 IP 地址范圍,但我無法找出相反的方法。

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

輸入 1: 5.10.64.0
輸入 2: 5.10.127.255
結果: 5.10.64.0/18

謝謝,達瓦爾

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

如果你還沒有從我的評論中弄清楚:

IP 數學必須以二進制形式完成。 IP 地址和掩碼是無符號整數(IPv4 為 32 位,IPv6 為 128 位)。 你只需要知道一個地址和掩碼,你就可以弄清楚其他一切。

這是您想要完成的算法,它適用於 IPv4 和 IPv6。

根據您的問題,您將獲得子網(輸入 1)和最后一個地址(輸入 2)。

  1. 從輸入 2 的無符號整數中減去輸入 1 的無符號整數。結果是反向子網掩碼。 反子網掩碼必須為0 ,或者反子網掩碼加1必須是2的冪,否則輸入之一( STOP, INPUT ERROR )有錯誤
  2. NOT的反向掩膜(步驟1的結果)是子網掩碼。
  3. 如果輸入1 AND子網掩碼不等於輸入1,你必須輸入(STOP,輸入錯誤的)一個錯誤。
  4. 掩碼長度(CIDR 編號)是子網掩碼中1位的數量。 有幾種方法可以計算二進制數中1位數,但如果子網掩碼是最大整數(或逆掩碼為0 ),則掩碼長度為32 (IPv4)或128 (IPv6)。 您可以循環,計算循環次數並將子網掩碼向左移動直到它等於0 ,循環計算循環次數並將逆掩碼向右移動直到它等於0然后將總數加1並減去總數從32 (IPv4)的或128 (IPv6)的,或減去的功率的指數2的總反向掩膜的加1 ,從32 (IPv4)的或128 (IPv6)的。
  5. 此時,您已驗證輸入 1(子網)、輸入 2(最后一個地址),並計算出掩碼長度(CIDR 編號)。
  6. 最終結果將是<Input 1>/<Mask Length>

你的例子:

步驟 1 ( 5.10.127.255 - 5.10.64.0 = 0.0.64.127 ):

101000010100111111111111111 - 01000010100100000000000000 = 11111111111111

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

NOT 00000000000000000011111111111111 = 11111111111111111100000000000000

第 3 步( 5.10.64.0 AND 255.255.192.0 = 5.10.64.0 ):

01000010100100000000000000 AND 11111111111111111100000000000000 = 01000010100100000000000000

第 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

第 5 步(輸入 1 = 5.10.64.0 ,輸入 2 = 5.10.127.255 ,掩碼長度 = 18

第 6 步(最終結果 = 5.10.64.0/18

開源 IPAddress Java 庫可以為您做到這一點。 免責聲明:我是 IPAddress 庫的項目經理。

這是執行此操作的示例代碼:

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

這是使用 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()};
}

對於您的示例,我們運行以下代碼:

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

輸出是:

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]

所以,我可以在這里找到 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();     
    }
}

感謝大家的見解和幫助!

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

我們研究了很多,這里是我們最終的實現方式,只需在添加以下類后使用RouteNotation.fromRange(...)方法獲取List<RouteNotation>

package my.vpn.utils;

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

public class RouteNotation {
    private int ipAddress;
    private int prefix;

    private RouteNotation(long ipAddress, int prefix) {
        this.ipAddress = (int) ipAddress;
        this.prefix = prefix;
    }

    public static RouteNotation fromIPv4(long ipAddress, int prefix) {
        return new RouteNotation(ipAddress, prefix);
    }

    public long getIP() {
        return this.ipAddress;
    }

    public void setIP(long v) {
        this.ipAddress = (int) v;
    }

    public int getPrefix() {
        return this.prefix;
    }

    public void setPrefix(int bitCount) {
        this.prefix = bitCount;
    }

    public long getFirst() {
        return this.ipAddress & getMaskFromPrefix(this.prefix);
    }

    public long getLast() {
        return Notation.getNext(this.ipAddress, this.prefix) - 1;
    }

    /**
     * Generates Notation list from given range.
     * <br>
     * Each IP range can result to multiple notations.
     *
     * @param first First IP included in returned notations.
     * @param last  Last IP included in notations.
     * @return Generated routing range notations.
     */
    public static List<RouteNotation> fromRange(long first, long last) {
        List<RouteNotation> result = new ArrayList<>();
        while (last >= first) {
            // Find prefix required to mask first IP in range.
            byte prefix = getPrefixFromRange(first, last);
            // At last, push current range to result list.
            result.add(RouteNotation.fromIPv4(first, prefix));
            // Skip current Notation range.
            first = getNext(first, prefix);
        }
        return result;
    }

    public static int getMaskFromPrefix(int bitCount) {
        return (int) (0xFFFFFFFF00000000L >> bitCount);
    }

    /**
     * Skips given Notation range and get first of next notation.
     * <p>
     * Used to get next IP right out of prefix range.
     */
    public static long getNext(long ip, int prefix) {
        // Forced to use left-shift or "getUnsigned(ip)", cause else Java would
        // cast result of "Math.pow(2, 32 - prefix)" method to integer
        // (and prevents any possible integer-overflow which we require).
        return (ip & getMaskFromPrefix(prefix)) + (1L << (32 - prefix));
    }

    public static byte getPrefixFromRange(long first, long possibleLast) {
        // Find max prefix required for distance (anything less is invalid).
        long distance = (possibleLast - first) + 1;
        double bitsRequiredForDistance = Math.log(distance) / Math.log(2);
        // Get max constant bit count.
        byte prefix = (byte) (32 - Math.ceil(bitsRequiredForDistance));
        // Validate and increase limit (more prefix means less range).
        while (prefix < 32) {
            // Limit difference to last IP in range (maximum).
            long max = RouteNotation.getNext(first, prefix) - 1;
            if (max > possibleLast) {
                ++prefix;
            }
            // Never allow IP less than first in range (minimum).
            else if ((first & getMaskFromPrefix(prefix)) < first) {
                ++prefix;
            } else {
                break;
            }
        }
        return prefix;
    }
}

請注意,這是一個最低需要的副本(沒有我們的單元測試和許多輔助方法),但您可能需要:

/**
 * @return Zero on any error, else IP v4 integer.
 */
public static long ipv4_cast(InetAddress address) {
    long result = 0;
    if (address != null) {
        byte data[] = address.getAddress();
        if (data.length <= 4) {
            for (byte b : data) {
                result <<= 8;
                result |= b & 0xFF;
            }
        }
    }
    return result;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM