Convert CIDR notation into IP range
Asked Answered
H

3

10

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.

Honghonied answered 15/8, 2015 at 18:51 Comment(0)
S
25

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

Scrophulariaceous answered 15/8, 2015 at 19:5 Comment(2)
Wow, this is a really nice answer! Your code is so short and to the point. +1!Honghonied
Instead of (mask ^ 0xffffffff) you can use ~mask to do the same thing.Charlton
L
2

Sami Kuhmonen, this is very nice code and seriously simplified. I noticed that it returns the network ID and the broadcast. However, for those that wish for it to return the usable address space. Simply add one to the starting IP and subtract one from the ending IP.

toip(ipstart + 1) + " - " + toip(ipend -1);
Lieb answered 28/2, 2020 at 16:8 Comment(0)
U
1

This could be done slightly easier with the BigInteger type (and it supports IPv6 too):

public (IPAddress, IPAddress) IPAddressRange(IPAddress address, byte cidrBits)
{
    var bytes = address.GetAddressBytes();

    var addrBits = bytes.Length * 8;

    var lowBitsMask = (BigInteger.One << (addrBits - cidrBits)) - 1;
    var highBitsMask = ~lowBitsMask;

    var ip = new BigInteger(bytes, isUnsigned: true, isBigEndian: true);
    var firstAddrBytes = ip & highBitsMask;
    var lastAddrBytes = ip | lowBitsMask;

    var span = new byte[bytes.Length];
    firstAddrBytes.TryWriteBytes(span, out _, true, true);
    var firstAddress = new IPAddress(span);

    span = new byte[bytes.Length];
    lastAddrBytes.TryWriteBytes(span, out _, true, true);
    var lastAddress = new IPAddress(span);

    return (firstAddress, lastAddress);
}

Console.WriteLine(IPAddressRange(IPAddress.Parse("5.39.40.96"),27));
Urien answered 14/7, 2023 at 8:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.