3

There has lots of example of convert CIDR to ip range. But I want to know how can I use start/end ip address to generate a/some cidr in C#?

for example: I have start ip address(192.168.0.1) and end ip address(192.168.0.254). So use these two address to generate cidr list {192.168.0.0/31, 192.168.0.2/32}. Is there any C# code example?

Chengzhao Li
  • 41
  • 1
  • 3

5 Answers5

8

It is difficult to determine the what exactly is being asked here (the CIDR list you give doesn't seem to correspond with the given input addresses), however the following code will allow you to find the smallest single CIDR that contains the specified start and end addresses.

You need to first convert the start and end IP addresses into 32 bit integers (e.g. 192.168.0.1 becomes 0xc0a80001), then apply the following algorithm:

var startAddr = 0xc0a80001; // 192.168.0.1
var endAddr = 0xc0a800fe;   // 192.168.0.254

// Determine all bits that are different between the two IPs
var diffs = startAddr ^ endAddr;

// Now count the number of consecutive zero bits starting at the most significant
var bits = 32;
var mask = 0;
while (diffs != 0)
{
    // We keep shifting diffs right until it's zero (i.e. we've shifted all the non-zero bits off)
    diffs >>= 1;
    // Every time we shift, that's one fewer consecutive zero bits in the prefix
    bits--;
    // Accumulate a mask which will have zeros in the consecutive zeros of the prefix and ones elsewhere
    mask = (mask << 1) | 1;
}

// Construct the root of the range by inverting the mask and ANDing it with the start address
var root = startAddr & ~mask;
// Finally, output the range
Console.WriteLine("{0}.{1}.{2}.{3}/{4}", root >> 24, (root >> 16) & 0xff, (root >> 8) & 0xff, root & 0xff, bits);

Running it on the two addresses in your question gives:

192.168.0.0/24
Iridium
  • 20,621
  • 6
  • 49
  • 68
  • 1
    This calculates the smallest CIDR block that includes two given IPv4 addresses, but I think the question was about a list of CIDRs that exactly covers the range. – Stefan Mar 08 '13 at 07:34
  • @Stefan it's rather difficult to determine what the OP wanted since the example output in the question doesn't appear to correspond to the start/end addresses given. As the OP's own answer with (supposedly) the desired output was posted a day later, after a number of people had considered mine sufficiently useful to up-vote it, I left it as-is. That wouldn't seem to warrant (I assume) your down-vote, but to each his own. – Iridium Mar 08 '13 at 10:01
  • Hm. How about editing your answer so it is clear how you interpreted the question? It is annoying to have to read the source to find out what it actually does. – Stefan Mar 08 '13 at 10:33
  • 1
    @Stefan I've edited the answer to clarify the intent of the code. Hopefully this is clearer now. – Iridium Mar 08 '13 at 13:02
5

CIDR class with static methods to split an IP range into a minimal set of disjoint CIDR ranges, which cover exactly the original IP range.

The split methods (the "real" one working on BigIntegers doing the actual work, and the wrapper for IP addresses and CIDR creation) are at the bottom.

Use with foreach (IPRangeToCidr.CIDR c in IPRangeToCidr.CIDR.split(first, last)) ...

Requires System.Numerics.dll in the references.

using System;
using System.Numerics;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;

namespace IPRangeToCidr {
    public struct CIDR {
        private IPAddress address;
        private uint network_length, bits;

        public CIDR(IPAddress address, uint network_length) {
            this.address = address;
            this.network_length = network_length;
            this.bits = AddressFamilyBits(address.AddressFamily);
            if (network_length > bits) {
                throw new ArgumentException("Invalid network length " + network_length + " for " + address.AddressFamily);
            }
        }

        public IPAddress NetworkAddress {
            get { return address; }
        }
        public IPAddress LastAddress {
            get { return IPAddressAdd(address, (new BigInteger(1) << (int) HostLength) - 1); }
        }
        public uint NetworkLength {
            get { return network_length; }
        }
        public uint AddressBits {
            get { return bits; }
        }
        public uint HostLength {
            get { return bits - network_length; }
        }

        override public String ToString() {
            return address.ToString() + "/" + NetworkLength.ToString();
        }

        public String ToShortString() {
            if (network_length == bits) return address.ToString();
            return address.ToString() + "/" + NetworkLength.ToString();
        }

        /* static helpers */
        public static IPAddress IPAddressAdd(IPAddress address, BigInteger i) {
            return IPFromUnsigned(IPToUnsigned(address) + i, address.AddressFamily);
        }

        public static uint AddressFamilyBits(AddressFamily family) {
            switch (family) {
            case AddressFamily.InterNetwork:
                return 32;
            case AddressFamily.InterNetworkV6:
                return 128;
            default:
                throw new ArgumentException("Invalid address family " + family);
            }
        }

        private static BigInteger IPToUnsigned(IPAddress addr) {
            /* Need to reverse addr bytes for BigInteger; prefix with 0 byte to force unsigned BigInteger
             * read BigInteger bytes as: bytes[n] bytes[n-1] ... bytes[0], address is bytes[0] bytes[1] .. bytes[n] */
            byte[] b = addr.GetAddressBytes();
            byte[] unsigned = new byte[b.Length + 1];
            for (int i = 0; i < b.Length; ++i) {
                unsigned[i] = b[(b.Length - 1) - i];
            }
            unsigned[b.Length] = 0;
            return new BigInteger(unsigned);
        }

        private static byte[] GetUnsignedBytes(BigInteger unsigned, uint bytes) {
            /* reverse bytes again. check that now higher bytes are actually used */
            if (unsigned.Sign < 0) throw new ArgumentException("argument must be >= 0");
            byte[] data = unsigned.ToByteArray();
            byte[] result = new byte[bytes];
            for (int i = 0; i < bytes && i < data.Length; ++i) {
                result[bytes - 1 - i] = data[i];
            }
            for (uint i = bytes; i < data.Length; ++i) {
                if (data[i] != 0) throw new ArgumentException("argument doesn't fit in requested number of bytes");
            }
            return result;
        }

        private static IPAddress IPFromUnsigned(BigInteger unsigned, System.Net.Sockets.AddressFamily family) {
            /* IPAddress(byte[]) constructor picks family from array size */
            switch (family) {
            case System.Net.Sockets.AddressFamily.InterNetwork:
                return new IPAddress(GetUnsignedBytes(unsigned, 4));
            case System.Net.Sockets.AddressFamily.InterNetworkV6:
                return new IPAddress(GetUnsignedBytes(unsigned, 16));
            default:
                throw new ArgumentException("AddressFamily " + family.ToString() + " not supported");
            }
        }

        /* splits set [first..last] of unsigned integers into disjoint slices { x,..., x + 2^k - 1 | x mod 2^k == 0 }
         *  covering exaclty the given set.
         * yields the slices ordered by x as tuples (x, k)
         * This code relies on the fact that BigInteger can't overflow; temporary results may need more bits than last is using.
         */
        public static IEnumerable<Tuple<BigInteger, uint>> split(BigInteger first, BigInteger last) {
            if (first > last) yield break;
            if (first < 0) throw new ArgumentException();
            last += 1;
            /* mask == 1 << len */
            BigInteger mask = 1;
            uint len = 0;
            while (first + mask <= last) {
                if ((first & mask) != 0) {
                    yield return new Tuple<BigInteger, uint>(first, len);
                    first += mask;
                }
                mask <<= 1;
                ++len;
            }
            while (first < last) {
                mask >>= 1;
                --len;
                if ((last & mask) != 0) {
                    yield return new Tuple<BigInteger, uint>(first, len);
                    first += mask;
                }
            }
        }

        public static IEnumerable<CIDR> split(IPAddress first, IPAddress last) {
            if (first.AddressFamily != last.AddressFamily) {
                throw new ArgumentException("AddressFamilies don't match");
            }
            AddressFamily family = first.AddressFamily;
            uint bits = AddressFamilyBits(family); /* split on numbers returns host length, CIDR takes network length */
            foreach (Tuple<BigInteger, uint> slice in split(IPToUnsigned(first), IPToUnsigned(last))) {
                yield return new CIDR(IPFromUnsigned(slice.Item1, family), bits - slice.Item2);
            }
        }
    }
}
Stefan
  • 4,624
  • 1
  • 18
  • 37
  • 1
    This code works well to generate the CIDRs for an IP range. This is the correct answer IMO. – Steven Wolfe Nov 24 '15 at 23:46
  • @Steven Wolfe: WTF ? The question was how to convert IP-range to CIDR, not CIDR to IP-range, so this is NOT the correct answer... – Stefan Steiger Nov 16 '16 at 18:01
  • 3
    @StefanSteiger Yes, this class *also* contains code to extract the first (`NetworkAddress`) and last address of a CIDR range, but the "main" function of the example `split` *does* convert IP-range to a list of CIDR blocks. Did you actually read the description at the top? – Stefan Nov 16 '16 at 19:35
0

I would recommend the use of IPNetwork Library https://github.com/lduchosal/ipnetwork. As of version 2, it supports IPv4 and IPv6 as well.

Supernet

  IPNetwork network = IPNetwork.Parse("192.168.0.1");
  IPNetwork network2 = IPNetwork.Parse("192.168.0.254");

  IPNetwork ipnetwork = IPNetwork.Supernet(network, network2);

  Console.WriteLine("Network : {0}", ipnetwork.Network);
  Console.WriteLine("Netmask : {0}", ipnetwork.Netmask);
  Console.WriteLine("Broadcast : {0}", ipnetwork.Broadcast);
  Console.WriteLine("FirstUsable : {0}", ipnetwork.FirstUsable);
  Console.WriteLine("LastUsable : {0}", ipnetwork.LastUsable);
  Console.WriteLine("Usable : {0}", ipnetwork.Usable);
  Console.WriteLine("Cidr : {0}", ipnetwork.Cidr);

Output

Network : 192.168.0.0
Netmask : 255.255.255.0
Broadcast : 192.168.0.255
FirstUsable : 192.168.0.1
LastUsable : 192.168.0.254
Usable : 254
Cidr : 24

Have fun !

LukeSkywalker
  • 301
  • 2
  • 7
-1

I found this C code and converted it into C# and it's working now.

MPelletier
  • 15,130
  • 14
  • 79
  • 128
Chengzhao Li
  • 41
  • 1
  • 3
-2

Necromancing.
No, there wasn't, and I don't understand why people keep upvoting wrong answers.

Here's the code for IP-range to CIDR & vice-versa:

// https://dev.maxmind.com/geoip/
// https://stackoverflow.com/questions/461742/how-to-convert-an-ipv4-address-into-a-integer-in-c
public static string IPrange2CIDR(string ip1, string ip2)
{
    uint startAddr = IP2num(ip1);
    uint endAddr = IP2num(ip2);

    // uint startAddr = 0xc0a80001; // 192.168.0.1
    // uint endAddr = 0xc0a800fe;   // 192.168.0.254
    // uint startAddr = System.BitConverter.ToUInt32(System.Net.IPAddress.Parse(ip1).GetAddressBytes(), 0);
    // uint endAddr = System.BitConverter.ToUInt32(System.Net.IPAddress.Parse(ip2).GetAddressBytes(), 0);

    if (startAddr > endAddr)
    {
        uint temp = startAddr;
        startAddr = endAddr;
        endAddr = temp;
    }

    // uint diff = endAddr - startAddr -1;
    // int bits =  32 - (int)System.Math.Ceiling(System.Math.Log10(diff) / System.Math.Log10(2));
    // return ip1 + "/" + bits;

    uint diffs = startAddr ^ endAddr;

    // Now count the number of consecutive zero bits starting at the most significant
    int bits = 32;
    // int mask = 0;

    // We keep shifting diffs right until it's zero (i.e. we've shifted all the non-zero bits off)
    while (diffs != 0)
    {
        diffs >>= 1;
        bits--; // Every time we shift, that's one fewer consecutive zero bits in the prefix
        // Accumulate a mask which will have zeros in the consecutive zeros of the prefix and ones elsewhere
        // mask = (mask << 1) | 1;
    }

    string res = ip1 + "/" + bits;
    System.Console.WriteLine(res);
    return res;
}




// https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking
public static void CIDR2IP(string IP)
{   
    string[] parts = IP.Split('.', '/');

    uint ipnum = (System.Convert.ToUInt32(parts[0]) << 24) |
        (System.Convert.ToUInt32(parts[1]) << 16) |
        (System.Convert.ToUInt32(parts[2]) << 8) |
        System.Convert.ToUInt32(parts[3]);

    int maskbits = System.Convert.ToInt32(parts[4]);
    uint mask = 0xffffffff;
    mask <<= (32 - maskbits);

    uint ipstart = ipnum & mask;
    uint ipend = ipnum | (mask ^ 0xffffffff);

    string fromRange = string.Format("{0}.{1}.{2}.{3}", ipstart >> 24, (ipstart >> 16) & 0xff, (ipstart >> 8) & 0xff, ipstart & 0xff);
    string toRange = string.Format("{0}.{1}.{2}.{3}", ipend >> 24, (ipend >> 16) & 0xff, (ipend >> 8) & 0xff, ipend & 0xff);

    System.Console.WriteLine(fromRange + " - " + toRange);
}



public static uint IP2num(string ip)
{
    string[] nums = ip.Split('.');
    uint first = System.UInt32.Parse(nums[0]);
    uint second = System.UInt32.Parse(nums[1]);
    uint third = System.UInt32.Parse(nums[2]);
    uint fourth = System.UInt32.Parse(nums[3]);

    return (first << 24) | (second << 16) | (third << 8) | (fourth);
}

public static void Test()
{
    string IP = "5.39.40.96/27";
    // IP = "88.84.128.0/19";
    CIDR2IP(IP);

    // IPrange2CIDR("88.84.128.0", "88.84.159.255");
    IPrange2CIDR("5.39.40.96", "5.39.40.127");

    System.Console.WriteLine(System.Environment.NewLine);
    System.Console.WriteLine(" --- Press any key to continue --- ");
    System.Console.ReadKey();
}
Stefan Steiger
  • 68,404
  • 63
  • 337
  • 408
  • 1
    This is basically the same as the answer by Iridium: "This calculates the smallest CIDR block that includes two given IPv4 addresses, ...", but you failed documenting that. – Stefan Jan 13 '17 at 16:53
  • @Stefan: Basically the same, except that the results are correct. Also, what sense does it make to calculate a NOT-smallest CIDR block ? Otherwise you might as well statically return "0.0.0.0/0"; that will always be correct - for IPv4. – Stefan Steiger Jan 24 '17 at 09:25
  • 1
    Just to clarify what @Stefan is talking about. CIDR's generally are "blocks". When converting a naive ipstart->ipend the above code will return one block, but it might overlap a neighboring CIDR - so you need to have multiple blocks to match the range correctly. Its ok if you know this is intended. But it is very likely that you need to have a more accurate output than a single overlapping span. – David Lannan Jul 23 '20 at 12:50