将CIDR表示法转换为IP范围

本文关键字:IP 范围 转换 CIDR 表示 | 更新日期: 2023-09-27 18:08:01

我们使用GeoLite2数据库来实现IP ->国家查找。出于性能考虑,我们希望导入CSV并将其转换为我们自己的格式。

CSV是这样表示的:

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
因此,我们需要将CIDR表示法(示例:5.39.40.96/27 )转换为IP地址范围。(From IP - To IP)

如何在c#中做到这一点?

注意:这不是这个问题的重复,因为我问的是c#实现,而不是Java。

将CIDR表示法转换为IP范围

这是一种处理它的方法,不使用任何库函数来明确发生了什么,并且在以后有人需要在其他语言中实现它时提供帮助。

代码首先将CIDR转换为32位数字,然后创建掩码确定起始地址,使用掩码的倒数确定结束地址,然后转换回CIDR格式。

请注意,没有错误检测,因此输入必须采用a.b.c.d/m格式。

IP地址的转换仅仅是用位移位将四个八位字节以大端字节形式(AABBCCDD)简单地连接起来。

掩码告诉我们从最高有效位开始有多少位是固定的,这意味着32是一个IP范围,0是整个IP范围。因此,我们可以取一个所有位都设置好的掩码,然后用32-maskbits向左移动它来确定实际的掩码。

如果我们将maskbits位设置为零,我们得到范围的开始,因此我们使用掩码位与IP。如果我们将位设置为1,则得到范围的结束,因此我们将与掩码的反位进行OR运算。

用CIDR格式打印IP地址也很简单:只需要将32位的值分成八位,然后用点分隔。

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));
        }
    }
}
输出:

5.39.40.96 - 5.39.40.127

Sami Kuhmonen,这是一个非常好的代码,并且认真地简化了。我注意到它返回网络ID和广播。然而,对于那些希望它返回可用地址空间的人。只需在起始IP上加1,在结束IP上减1。

toip(ipstart + 1) + " - " + toip(ipend -1);

使用BigInteger类型(它也支持IPv6)可以稍微容易一些:

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));