seancfoley / IPAddress

Java library for handling IP addresses and subnets, both IPv4 and IPv6
https://seancfoley.github.io/IPAddress/
Apache License 2.0
463 stars 62 forks source link

Getting all subnets in a network #17

Closed monicarajasekaran closed 5 years ago

monicarajasekaran commented 5 years ago

Hi, this is not an issue per say but rather a question. If i have a CIDR address for example "192.168.0.0/24", how would i get an iterator to all subnets in this network? Thanks for your help in advance.

seancfoley commented 5 years ago

I am not exactly certain what you are asking, when you say "all subnets". Perhaps you mean all prefix prefix blocks for all prefix lengths larger than the given prefix.

If that is the case, then "192.168.0.0/24" has 510 subnets (2 + 4 + 8 + ... + 256), so for illustration I will use "192.168.0.0/30" instead which has 6 (2 + 4). Basically you just shift the prefix length and then iterate over the prefix blocks. Here are two methods, one that does it recursively, another iteratively:

public static void main(String[] args) {
    //IPAddress subnet = new IPAddressString("192.168.0.0/24").getAddress();
    IPAddress subnet = new IPAddressString("192.168.0.0/30").getAddress();
    // ordered by prefix length
    iterateBlocks(subnet);
    count = 0;
    System.out.println();
    // ordered by value, low to high
    recurseBlocks(subnet);
}

static int count;

public static void recurseBlocks(IPAddress subnet) {
    int subnetPrefixLength = subnet.getPrefixLength();
    if(subnetPrefixLength < subnet.getBitCount()) {
        IPAddress blocks = subnet.setPrefixLength(subnetPrefixLength + 1, false);
        Iterator<? extends IPAddress> blocksIterator = blocks.prefixBlockIterator();
        while(blocksIterator.hasNext()) {
            IPAddress next = blocksIterator.next();
            System.out.println("subnet " + ++count + " " + next + 
                " with size " + next.getCount());
            recurseBlocks(next);
        }
    }
}

public static void iterateBlocks(IPAddress subnet) {
    int subnetPrefixLength = subnet.getPrefixLength();
    while(subnetPrefixLength < subnet.getBitCount()) {
        IPAddress blocks = subnet.setPrefixLength(++subnetPrefixLength, false);
        Iterator<? extends IPAddress> blocksIterator = blocks.prefixBlockIterator();
        while(blocksIterator.hasNext()) {
            IPAddress next = blocksIterator.next();
            System.out.println("subnet " + ++count + " " + next + 
                " with size " + next.getCount());
        }
    }
}

Output:

subnet 1 192.168.0.0/31 with size 2
subnet 2 192.168.0.2/31 with size 2
subnet 3 192.168.0.0/32 with size 1
subnet 4 192.168.0.1/32 with size 1
subnet 5 192.168.0.2/32 with size 1
subnet 6 192.168.0.3/32 with size 1

subnet 1 192.168.0.0/31 with size 2
subnet 2 192.168.0.0/32 with size 1
subnet 3 192.168.0.1/32 with size 1
subnet 4 192.168.0.2/31 with size 2
subnet 5 192.168.0.2/32 with size 1
subnet 6 192.168.0.3/32 with size 1
kulpatel commented 1 year ago

Hello seancfoley, what monicarajasekaran means here is given i.e class-B address 128.0.0.0/28 means Network bit = 8 +4 = 12= 2^12 = 4096 #subnets possible, andHost bit = 32-28 = 4, 2^4-2 = 14 host possible per subnet. Now how shall we get list of alll subnets and (ofcourse when we have subnet we can get the list of IP address possible out of that subnet). All i m interested is get list of all 4096 subnets values not just count. any ideal will really help here.

seancfoley commented 1 year ago

@kulpatel The answer remains the same.

// Start with 128.0.0.0/16
IPAddress classB = new IPAddressString("128.0.0.0/16").getAddress();

// adjust prefix (but don't zero, second arg is false)
IPAddress adjusted = classB.adjustPrefixLength(12, false);
System.out.println(adjusted);
System.out.println();

// iterate
Iterator<? extends IPAddress> iter = adjusted.prefixBlockIterator();
IPAddress blocks[] = new IPAddress[adjusted.getPrefixCount().intValue()];
int i = 0;
while(iter.hasNext()) {
    blocks[i++] = iter.next();
}

// or you can use streams
Stream<? extends IPAddress> stream = adjusted.prefixBlockStream();
IPAddress blocks2[] = stream.toArray(IPAddress[]::new);

i = 0;
for(IPAddress block: blocks) {
    System.out.println(block);
    if(++i > 4) {
        break;
    }
}
System.out.println();

i = 0;
for(IPAddress block: blocks2) {
    System.out.println(block);
    if(++i > 4) {
        break;
    }
}

Output:

128.0.*.0-240/28

128.0.0.0/28
128.0.0.16/28
128.0.0.32/28
128.0.0.48/28
128.0.0.64/28

128.0.0.0/28
128.0.0.16/28
128.0.0.32/28
128.0.0.48/28
128.0.0.64/28

Also see: https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#break-a-cidr-prefix-block-into-direct-component-blocks or https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#iteratively-break-a-cidr-prefix-block-into-component-blocks or https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#derive-new-subnet-from-existing-cidr-subnet

kulpatel commented 1 year ago

@seancfoley thanks for your reply and it helped me to adjust according to my need. One Question here/doubt. i.e. input is 5000::0/24 and have customised above code a bit my requirement which is below.

Requirement: if i wanna see if given ip/subnet is sufficient to cater my need (# subnet pool out of it), i dont wann shift and just get possible #ipaddresses out of it and get it done. if not (assume i need 3 subnets and 50 host per subnet from given cidr) will shift it and see if it justifies and keep doing until either it matches my need or not possible (error out). so i did it below way. Intentionally added my code to get ur exact input. above code doesn't work if i replace = classB.adjustPrefixLength(12, false); with = classB.setPrefixLength(12, false); for same address (5000::0/24 ). 1> does this mean with 5000::0/24 only 1 subnetwork possible or m i doing something wrong here?

My intention is not to force you to write my code wanna take ur opinion and write it optimal way as it could go terribly wrong in v6 case.

NOTE: All works fine with V4, only V6 i see some problem.

public String getListOfNetworks(IPAddress subnet, Integer numHosts, Integer numVlans) {
        StringBuilder info_logs = new StringBuilder();
        try {
            //    listByDecreasingSize();
            int bitShift = 1;//wanna see if the same subnet justifies the need or not, else increment.
            int subnetPrefixLength = subnet.getPrefixLength();
            List<IPAddress> iteratedSubnets = new ArrayList<>();
            IPAddress blocks = subnet.setPrefixLength(
                    subnetPrefixLength, false);
            if ((subnet.getCount().subtract(new BigInteger("2")).compareTo(new BigInteger(numHosts.toString()))) > 0) { //-2 because network and broadcast address not usable.
                // iterate through all prefixes
                Iterator<? extends IPAddress> blocksIterator =
                        blocks.prefixBlockIterator();
                while (true) {
                    IPAddress next = blocksIterator.next();
                    if (blocksIterator.hasNext()) {
                        iteratedSubnets.add(next);
                    } else {
                        iteratedSubnets.add(next);
                        break;
                    }
                }
                if (iteratedSubnets.size() >= numVlans) {
                    for (IPAddress sna : iteratedSubnets) {
                        info_logs.append(sna + " size " + sna.getCount() + " from " + sna.getLower() + " to " + sna.getUpper() + "\r");
                        this.na.listOfSubNetworks.add(new SubNetworkAddress(sna, InetAddressUtils.subnetToIps(sna.toString())));
                    }
                    this.na.prefixLen = subnetPrefixLength;
                    return info_logs.toString();
                } else {
                    iteratedSubnets.removeAll(iteratedSubnets);
                }
            }
            while (subnetPrefixLength + bitShift <= subnet.getBitCount()) {
                // adjust original subnet prefix length
                subnetPrefixLength += bitShift;
                blocks = subnet.setPrefixLength(
                        subnetPrefixLength, false);
                if ((subnet.getCount().subtract(new BigInteger("2")).compareTo(new BigInteger(numHosts.toString()))) > 0) {
                    // iterate through all prefixes
                    Iterator<? extends IPAddress> blocksIterator =
                            blocks.prefixBlockIterator();
                    while (true) {
                        IPAddress next = blocksIterator.next();
                        if (blocksIterator.hasNext()) {
                            iteratedSubnets.add(next);
                        } else {
                            iteratedSubnets.add(next);
                            break;
                        }
                    }
                    if (iteratedSubnets.size() >= numVlans) {
                        for (IPAddress sna : iteratedSubnets) {
                            info_logs.append(sna + " size " + sna.getCount() + " from " + sna.getLower() + " to " + sna.getUpper() + "\r");
                            this.na.listOfSubNetworks.add(new SubNetworkAddress(sna, InetAddressUtils.subnetToIps(sna.toString())));
                        }
                        this.na.prefixLen = subnetPrefixLength;
                        return info_logs.toString();
                    } else {
                        iteratedSubnets.removeAll(iteratedSubnets);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return info_logs.toString();
    }
seancfoley commented 1 year ago

There is no difference with IPv6. As for your code, I cannot comment, since I do not do code reviews.

"code doesn't work if i replace = classB.adjustPrefixLength(12, false); with = classB.setPrefixLength(12, false); for same address (5000::0/24 )"

adjustPrefixLength and setPrefixLength are not the same. I don't know why you would expect them to be interchangeable. adjustPrefixLength increases or decreases the existing prefix length. setPrefixLength ignores the existing prefix length and just sets the new prefix length. Read the javadoc.

5000::0/24 is equivalent to 5000:0-ff:*:*:*:*:*:*. If you shift the prefix end to the right, increasing it, you get many subnets. You end up with a range of values iniside the prefix. If you shift the prefix end to the left, shortening it, the subnet count is the same, there is no range of values inside the prefix.

adjustPrefixLength(12, false) on 5000::0/24 increases prefix length 24 by 12 so you get 5000:0-ff:0-f000::/36 This gives 4096 subnets, as shown by the range 0-ff:0-f that is now part of the prefix.

setPrefixLength(12, false) on 5000::0/24 makes the new prefix length 12 so you get 5000::/12, shortening the prefix. So you have just one subnet, which is the single value 500.