Convert CIDR notation into IP range

bytecode77 picture bytecode77 · Aug 15, 2015 · Viewed 9.7k times · Source

We are using the GeoLite2 database in order to implement an IP -> country lookup. For performance reasons, we want to import the CSV and convert it to our own format.

The CSV is represented like this:

5.39.40.96/27,3017382,3017382,,0,0
5.39.40.128/28,3017382,3017382,,0,0
5.39.40.144/28,2635167,3017382,,0,0
5.39.40.160/27,3017382,3017382,,0,0
5.39.40.192/26,3017382,3017382,,0,0
5.39.41.0/25,3017382,3017382,,0,0
5.39.41.128/26,3017382,3017382,,0,0
5.39.41.192/26,2635167,3017382,,0,0
5.39.42.0/24,3017382,3017382,,0,0
5.39.43.0/25,3017382,3017382,,0,0

So we need to convert the CIDR notation (example: 5.39.40.96/27) into an IP address range. (From IP - To IP)

How can this be done in C#?

Note: This is not a duplicate of this question, since I'm asking about a C# implementation and not Java.

Answer

Sami Kuhmonen picture Sami Kuhmonen · Aug 15, 2015

Here is one way to handle it, without using any library functions to make it clear what's happening and to help if someone needs to implement it in other languages later on.

The code first converts the CIDR into a 32bit number, then creates the mask to determine the start address, uses the inverse of the mask to determine the end address and then converts back to CIDR format.

Note that there is no error detection so the input must be in the a.b.c.d/m format.

The conversion of the IP address is just a simple concatenation of the four octets in big endian form (AABBCCDD) using bit shifts.

The mask tells how many bits from the most significant bit are fixed, meaning 32 is a single IP range and 0 would be the whole IP range. Thus we can take a mask with all bits set and shift it left with 32-maskbits to determine the actual mask.

If we set the maskbits bits to zero, we get the beginning of the range, so we AND the IP with maskbits. If we set the bits to one, we get the end of the range, so we will OR with the negated bits of mask.

Printing the IP address in CIDR format is again simple: just split the 32bit value into octets and write them separated with dots.

using System;

namespace CSTests
{
    class Program
    {
        static string toip(uint ip)
        {
            return String.Format("{0}.{1}.{2}.{3}", ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
        }

        static void Main(string[] args)
        {
            string IP = "5.39.40.96/27";
            string[] parts = IP.Split('.', '/');

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

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

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

            Console.WriteLine(toip(ipstart) + " - " + toip(ipend));
        }
    }
}

Output:

5.39.40.96 - 5.39.40.127