如何在C#中将IP范围转换为Cidr?

时间:2012-11-22 08:05:47

标签: c# ip cidr

有很多将CIDR转换为ip范围的示例。但我想知道如何使用开始/结束IP地址在C#中生成/某些cidr?

例如: 我有启动IP地址(192.168.0.1)和结束IP地址(192.168.0.254)。因此,使用这两个地址生成cidr列表{192.168.0.0/31,192.168.0.2/32}。有没有C#代码示例?

6 个答案:

答案 0 :(得分:8)

很难确定这里要求的确切内容(您提供的CIDR列表似乎与给定的输入地址不对应),但是以下代码将允许您找到包含该CIDR的最小单个CIDR指定的起始和结束地址。

首先需要将起始和结束IP地址转换为32位整数(例如192.168.0.1变为0xc0a80001),然后应用以下算法:

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

在你问题的两个地址上运行它会给出:

192.168.0.0/24

答案 1 :(得分:4)

CIDR类使用静态方法将IP范围拆分为一组最小的不相交CIDR范围,这些范围完全覆盖原始IP范围。

分裂方法(“真实”的方法,用于BigIntegers进行实际工作,以及IP地址和CIDR创建的包装)位于底部。

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

一起使用

引用中需要System.Numerics.dll。

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

答案 2 :(得分:0)

我建议使用IPNetwork Library https://github.com/lduchosal/ipnetwork。 从版本2开始,它也支持IPv4和IPv6。

<强>超网

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

输出

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

玩得开心!

答案 3 :(得分:0)

我将它用于 IpV4,如果其中有问题,请告诉我。 您可以从以下链接中找到提取源代码: https://blog.ip2location.com/knowledge-base/how-to-convert-ip-address-range-into-cidr/

using System;
using System.Collections.Generic;
using System.Net;

namespace ConsoleApp
{
    public class IPNetwork
    {
        private readonly long _firstIpAddress;
        private readonly long _lastIpAddress;

        public static IPNetwork[] FromIpRange(IPAddress firstIpAddress, IPAddress lastIpAddress)
            => FromIpRange(IpAddressToLong(firstIpAddress), IpAddressToLong(lastIpAddress));

        public static IPNetwork[] FromIpRange(long firstIpAddress, long lastIpAddress)
        {
            var result = new List<IPNetwork>();
            while (lastIpAddress >= firstIpAddress)
            {
                byte maxSize = 32;
                while (maxSize > 0)
                {
                    long mask = IMask(maxSize - 1);
                    long maskBase = firstIpAddress & mask;

                    if (maskBase != firstIpAddress)
                        break;

                    maxSize--;
                }
                double x = Math.Log(lastIpAddress - firstIpAddress + 1) / Math.Log(2);
                byte maxDiff = (byte)(32 - Math.Floor(x));
                if (maxSize < maxDiff)
                {
                    maxSize = maxDiff;
                }
                var ipAddress = IpAddressFromLong(firstIpAddress);
                result.Add(new IPNetwork(ipAddress, maxSize));
                firstIpAddress += (long)Math.Pow(2, 32 - maxSize);
            }
            return result.ToArray();
        }

        private static long IMask(int s)
        {
            return (long)(Math.Pow(2, 32) - Math.Pow(2, 32 - s));
        }

        public static long IpAddressToLong(IPAddress ipAddress)
        {
            var bytes = ipAddress.GetAddressBytes();
            return ((long)bytes[0] << 24) | ((long)bytes[1] << 16) | ((long)bytes[2] << 8) | bytes[3];
        }

        public static IPAddress IpAddressFromLong(long ipAddress)
            => new IPAddress((uint)IPAddress.NetworkToHostOrder((int)ipAddress));

        public IPNetwork(IPAddress prefix, int prefixLength = 32)
        {
            if (prefix.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
                throw new NotSupportedException("IPv6 is not supported");

            Prefix = prefix;
            PrefixLength = prefixLength;

            var mask = (uint)~(0xFFFFFFFFL >> prefixLength);
            _firstIpAddress = IpAddressToLong(Prefix) & mask;
            _lastIpAddress = _firstIpAddress | ~mask;
        }

        public static IPNetwork Parse(string value)
        {
            try
            {
                var parts = value.Split('/');
                return new IPNetwork(IPAddress.Parse(parts[0]), int.Parse(parts[1]));
            }
            catch
            {
                throw new FormatException($"Could not parse IPNetwork from {value}");
            }
        }

        public override string ToString() => $"{Prefix}/{PrefixLength}";

        public IPAddress Prefix { get; }
        public int PrefixLength { get; }
        public IPAddress LastAddress => IpAddressFromLong(_lastIpAddress);
        public IPAddress FirstAddress => IpAddressFromLong(_firstIpAddress);
        public long Total => _lastIpAddress - _firstIpAddress + 1;
    }
}

用法 1:

var startAddress = IPAddress.Parse("192.168.0.0");
var endAddress = IPAddress.Parse("192.168.0.255");
foreach (var item in IPNetwork.FromIpRange(startAddress, endAddress))
        Console.WriteLine(item);

结果

192.168.0.0/24

用法 2:

var startAddress = IPAddress.Parse("192.168.0.1");
var endAddress = IPAddress.Parse("192.168.0.254");
foreach (var item in IPNetwork.FromIpRange(startAddress, endAddress))
        Console.WriteLine(item);

结果:

192.168.0.1/32
192.168.0.2/31
192.168.0.4/30
192.168.0.8/29
192.168.0.16/28
192.168.0.32/27
192.168.0.64/26
192.168.0.128/26
192.168.0.192/27
192.168.0.224/28
192.168.0.240/29
192.168.0.248/30
192.168.0.252/31
192.168.0.254/32

答案 4 :(得分:-1)

我找到了这个C code并将其转换为C#,它现在正在运行。

答案 5 :(得分:-1)

Necromancing。
不,没有,我不明白为什么人们不断提出错误的答案。

这是IP范围到CIDR&amp;的代码。反之亦然:

// 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();
}