Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert IP range to Cidr in C#?

Tags:

c#

ip

cidr

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?

like image 296
Chengzhao Li Avatar asked Dec 16 '22 17:12

Chengzhao Li


2 Answers

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
like image 154
Iridium Avatar answered Dec 31 '22 13:12

Iridium


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);
            }
        }
    }
}
like image 32
Stefan Avatar answered Dec 31 '22 13:12

Stefan