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

Che*_* Li 2 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#代码示例?

Iri*_*ium 8

很难确定这里究竟要问的是什么(你给出的CIDR列表似乎与给定的输入地址不对应),但是下面的代码将允许你找到包含指定的start和c的最小的单个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);
Run Code Online (Sandbox Code Playgroud)

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

192.168.0.0/24
Run Code Online (Sandbox Code Playgroud)


Ste*_*fan 5

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

拆分方法(在 BigInteger 上进行实际工作的“真实”方法,以及 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @StefanSteiger 是的,这个类*也*包含提取CIDR范围的第一个(`NetworkAddress`)和最后一个地址的代码,但是示例`split`的“main”函数*确实*将IP范围转换为列表CIDR 块。你真的阅读了顶部的描述吗? (3认同)