C# 如何检查 IP 地址是否在特定子网内

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1499269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 18:11:06  来源:igfitidea点击:

How to check if an IP address is within a particular subnet

c#ip-addresssubnet

提问by Ryan Michela

I have a subnet in the format 10.132.0.0/20 and an IP address from the ASP.Net request object.

我有一个格式为 10.132.0.0/20 的子网和一个来自 ASP.Net 请求对象的 IP 地址。

Is there a .NET framework function to check to see if the IP address is within the given subnet?

是否有 .NET 框架函数来检查 IP 地址是否在给定的子网内?

If not, how can it be done? Bit manipulation, I guess?

如果没有,怎么办?位操作,我猜?

采纳答案by Compufreak

Using the answers from Thomasand Christogether with Ciscos Subnetting ExamplesI finally got something to work for IPv4 and IPv6 if you use the CIDR notation (IPAddress/PrefixLength). My IPv6-Implementation might be a bit too straight forward but as there is no UInt128-datatype I couldn't adapt Thomas's solution. Here is the code that seems to work well:

使用的答案从托马斯克里斯一起思科子网划分的例子,如果你使用CIDR标记(ip地址/ PrefixLength)我终于得到了一些为IPv4和IPv6的工作。我的 IPv6-Implementation 可能有点过于直接,但由于没有 UInt128-datatype 我无法适应 Thomas 的解决方案。这是似乎运行良好的代码:

public static bool IsInSubnet(this IPAddress address, string subnetMask)
        {
            var slashIdx = subnetMask.IndexOf("/");
            if (slashIdx == -1)
            { // We only handle netmasks in format "IP/PrefixLength".
                throw new NotSupportedException("Only SubNetMasks with a given prefix length are supported.");
            }

            // First parse the address of the netmask before the prefix length.
            var maskAddress = IPAddress.Parse(subnetMask.Substring(0, slashIdx));

            if (maskAddress.AddressFamily != address.AddressFamily)
            { // We got something like an IPV4-Address for an IPv6-Mask. This is not valid.
                return false;
            }

            // Now find out how long the prefix is.
            int maskLength = int.Parse(subnetMask.Substring(slashIdx + 1));

            if (maskAddress.AddressFamily == AddressFamily.InterNetwork)
            {
                // Convert the mask address to an unsigned integer.
                var maskAddressBits = BitConverter.ToUInt32(maskAddress.GetAddressBytes().Reverse().ToArray(), 0);

                // And convert the IpAddress to an unsigned integer.
                var ipAddressBits = BitConverter.ToUInt32(address.GetAddressBytes().Reverse().ToArray(), 0);

                // Get the mask/network address as unsigned integer.
                uint mask = uint.MaxValue << (32 - maskLength);

                // https://stackoverflow.com/a/1499284/3085985
                // Bitwise AND mask and MaskAddress, this should be the same as mask and IpAddress
                // as the end of the mask is 0000 which leads to both addresses to end with 0000
                // and to start with the prefix.
                return (maskAddressBits & mask) == (ipAddressBits & mask);
            }

            if (maskAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                // Convert the mask address to a BitArray.
                var maskAddressBits = new BitArray(maskAddress.GetAddressBytes());

                // And convert the IpAddress to a BitArray.
                var ipAddressBits = new BitArray(address.GetAddressBytes());

                if (maskAddressBits.Length != ipAddressBits.Length)
                {
                    throw new ArgumentException("Length of IP Address and Subnet Mask do not match.");
                }

                // Compare the prefix bits.
                for (int i = 0; i < maskLength; i++)
                {
                    if (ipAddressBits[i] != maskAddressBits[i])
                    {
                        return false;
                    }
                }

                return true;
            }

            throw new NotSupportedException("Only InterNetworkV6 or InterNetwork address families are supported.");
        }

And this are the XUnit tests I tested it with:

这是我测试的 XUnit 测试:

public class IpAddressExtensionsTests
    {
        [Theory]
        [InlineData("192.168.5.85/24", "192.168.5.1")]
        [InlineData("192.168.5.85/24", "192.168.5.254")]
        [InlineData("10.128.240.50/30", "10.128.240.48")]
        [InlineData("10.128.240.50/30", "10.128.240.49")]
        [InlineData("10.128.240.50/30", "10.128.240.50")]
        [InlineData("10.128.240.50/30", "10.128.240.51")]
        public void IpV4SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
        {
            var ipAddressObj = IPAddress.Parse(ipAddress);
            Assert.True(ipAddressObj.IsInSubnet(netMask));
        }

        [Theory]
        [InlineData("192.168.5.85/24", "192.168.4.254")]
        [InlineData("192.168.5.85/24", "191.168.5.254")]
        [InlineData("10.128.240.50/30", "10.128.240.47")]
        [InlineData("10.128.240.50/30", "10.128.240.52")]
        [InlineData("10.128.240.50/30", "10.128.239.50")]
        [InlineData("10.128.240.50/30", "10.127.240.51")]
        public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
        {
            var ipAddressObj = IPAddress.Parse(ipAddress);
            Assert.False(ipAddressObj.IsInSubnet(netMask));
        }

        [Theory]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFFF")]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0001:0000:0000:0000")]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFF0")]
        [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")]
        public void IpV6SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
        {
            var ipAddressObj = IPAddress.Parse(ipAddress);
            Assert.True(ipAddressObj.IsInSubnet(netMask));
        }

        [Theory]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFFF")]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0000:0000:0000:0000")]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0001:0000:0000:0000")]
        [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFF0")]
        [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")]
        public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
        {
            var ipAddressObj = IPAddress.Parse(ipAddress);
            Assert.False(ipAddressObj.IsInSubnet(netMask));
        }
    }

As base for the tests I used Ciscos Subnetting Examplesand IBMs IPV6 address examples.

作为测试的基础,我使用了Ciscos Subnetting ExamplesIBMs IPV6 地址示例

I hope someone finds this helpful ;)

我希望有人觉得这有帮助;)

回答by Thomas

Bit manipulation works. Stuff the IP into a 32-bits unsigned integer, do the same with the subnet's address, &-mask both with 0xFFFFFFFF << (32-20)and compare:

位操作有效。将 IP 填充为 32 位无符号整数,对子网地址执行相同操作,&使用0xFFFFFFFF << (32-20)和比较-mask :

unsigned int net = ..., ip = ...;
int network_bits = 20;
unsigned int mask = 0xFFFFFFFF << (32 - network_bits);
if ((net & mask) == (ip & mask)) {
  // ...
}

回答by Chris Shouts

Take a look at IP Address Calculations with C#on MSDN blogs. It contains an extension method (IsInSameSubnet) that should meet your needs as well as some other goodies.

在 MSDN 博客上查看使用 C#进行IP 地址计算。它包含一个扩展方法 ( IsInSameSubnet) ,应该可以满足您的需求以及其他一些好东西。

public static class IPAddressExtensions
{
    public static IPAddress GetBroadcastAddress(this IPAddress address, IPAddress subnetMask)
    {
        byte[] ipAdressBytes = address.GetAddressBytes();
        byte[] subnetMaskBytes = subnetMask.GetAddressBytes();

        if (ipAdressBytes.Length != subnetMaskBytes.Length)
            throw new ArgumentException("Lengths of IP address and subnet mask do not match.");

        byte[] broadcastAddress = new byte[ipAdressBytes.Length];
        for (int i = 0; i < broadcastAddress.Length; i++)
        {
            broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
        }
        return new IPAddress(broadcastAddress);
    }

    public static IPAddress GetNetworkAddress(this IPAddress address, IPAddress subnetMask)
    {
        byte[] ipAdressBytes = address.GetAddressBytes();
        byte[] subnetMaskBytes = subnetMask.GetAddressBytes();

        if (ipAdressBytes.Length != subnetMaskBytes.Length)
            throw new ArgumentException("Lengths of IP address and subnet mask do not match.");

        byte[] broadcastAddress = new byte[ipAdressBytes.Length];
        for (int i = 0; i < broadcastAddress.Length; i++)
        {
            broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
        }
        return new IPAddress(broadcastAddress);
    }

    public static bool IsInSameSubnet(this IPAddress address2, IPAddress address, IPAddress subnetMask)
    {
        IPAddress network1 = address.GetNetworkAddress(subnetMask);
        IPAddress network2 = address2.GetNetworkAddress(subnetMask);

        return network1.Equals(network2);
    }
}

回答by nu everest

The solution is to convert the IP Address into bytes using System.Net.IPAddressand perform bitwise comparisons on the address, subnet, and mask octets.

解决方案是将 IP 地址转换为字节System.Net.IPAddress,并对地址、子网和掩码八位字节进行按位比较。

The Binary AND Operator &copies a bit to the result if it exists in both operands.

&如果两个操作数中都存在二进制 AND 运算符,则将其复制到结果中。

The code:

编码:

using System.Net;   // Used to access IPAddress

bool IsAddressOnSubnet(string address, string subnet, string mask)
{
    try
    {
        IPAddress Address = IPAddress.Parse(address);
        IPAddress Subnet = IPAddress.Parse(subnet);
        IPAddress Mask = IPAddress.Parse(mask);            

        Byte[] addressOctets = Address.GetAddressBytes();
        Byte[] subnetOctets = Mask.GetAddressBytes();
        Byte[] networkOctets = Subnet.GetAddressBytes();

        return
            ((networkOctets[0] & subnetOctets[0]) == (addressOctets[0] & subnetOctets[0])) &&
            ((networkOctets[1] & subnetOctets[1]) == (addressOctets[1] & subnetOctets[1])) &&
            ((networkOctets[2] & subnetOctets[2]) == (addressOctets[2] & subnetOctets[2])) &&
            ((networkOctets[3] & subnetOctets[3]) == (addressOctets[3] & subnetOctets[3]));
    }
    catch (System.Exception ex)
    {
        return false;                
    }
}

Special thanks to Reference

特别感谢 参考

回答by Oliver Schimmer

Since the MSDN blog code relies on a broadcast and IPv6 doesn't have one, I don't know if it works with IPv6.

由于MSDN博客代码依赖广播,而IPv6没有广播,我不知道它是否适用于IPv6。

I ended up with these methods (thanks to nu everest). You can get the subnet and mask from a CIDR notation ("1.2.3.4/5") and check whether an adress is within this network or not.

我最终使用了这些方法(感谢 nu everest)。您可以从 CIDR 表示法(“1.2.3.4/5”)获取子网和掩码,并检查地址是否在此网络内。

This works for IPv4 and IPv6:

这适用于 IPv4 和 IPv6:

public static class IpAddresses
{
    public static Tuple<IPAddress, IPAddress> GetSubnetAndMaskFromCidr(string cidr)
    {
        var delimiterIndex = cidr.IndexOf('/');
        string ipSubnet = cidr.Substring(0, delimiterIndex);
        string mask = cidr.Substring(delimiterIndex + 1);

        var subnetAddress = IPAddress.Parse(ipSubnet);

        if (subnetAddress.AddressFamily == AddressFamily.InterNetworkV6)
        {
            // ipv6
            var ip = BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber) << (128 - int.Parse(mask));

            var maskBytes = new[]
            {
                (byte)((ip & BigInteger.Parse("00FF000000000000000000000000000000", NumberStyles.HexNumber)) >> 120),
                (byte)((ip & BigInteger.Parse("0000FF0000000000000000000000000000", NumberStyles.HexNumber)) >> 112),
                (byte)((ip & BigInteger.Parse("000000FF00000000000000000000000000", NumberStyles.HexNumber)) >> 104),
                (byte)((ip & BigInteger.Parse("00000000FF000000000000000000000000", NumberStyles.HexNumber)) >> 96),
                (byte)((ip & BigInteger.Parse("0000000000FF0000000000000000000000", NumberStyles.HexNumber)) >> 88),
                (byte)((ip & BigInteger.Parse("000000000000FF00000000000000000000", NumberStyles.HexNumber)) >> 80),
                (byte)((ip & BigInteger.Parse("00000000000000FF000000000000000000", NumberStyles.HexNumber)) >> 72),
                (byte)((ip & BigInteger.Parse("0000000000000000FF0000000000000000", NumberStyles.HexNumber)) >> 64),
                (byte)((ip & BigInteger.Parse("000000000000000000FF00000000000000", NumberStyles.HexNumber)) >> 56),
                (byte)((ip & BigInteger.Parse("00000000000000000000FF000000000000", NumberStyles.HexNumber)) >> 48),
                (byte)((ip & BigInteger.Parse("0000000000000000000000FF0000000000", NumberStyles.HexNumber)) >> 40),
                (byte)((ip & BigInteger.Parse("000000000000000000000000FF00000000", NumberStyles.HexNumber)) >> 32),
                (byte)((ip & BigInteger.Parse("00000000000000000000000000FF000000", NumberStyles.HexNumber)) >> 24),
                (byte)((ip & BigInteger.Parse("0000000000000000000000000000FF0000", NumberStyles.HexNumber)) >> 16),
                (byte)((ip & BigInteger.Parse("000000000000000000000000000000FF00", NumberStyles.HexNumber)) >> 8),
                (byte)((ip & BigInteger.Parse("00000000000000000000000000000000FF", NumberStyles.HexNumber)) >> 0),
            };

            return Tuple.Create(subnetAddress, new IPAddress(maskBytes));
        }
        else
        {
            // ipv4
            uint ip = 0xFFFFFFFF << (32 - int.Parse(mask));

            var maskBytes = new[]
            {
                (byte)((ip & 0xFF000000) >> 24),
                (byte)((ip & 0x00FF0000) >> 16),
                (byte)((ip & 0x0000FF00) >> 8),
                (byte)((ip & 0x000000FF) >> 0),
            };

            return Tuple.Create(subnetAddress, new IPAddress(maskBytes));
        }
    }

    public static bool IsAddressOnSubnet(IPAddress address, IPAddress subnet, IPAddress mask)
    {
        byte[] addressOctets = address.GetAddressBytes();
        byte[] subnetOctets = mask.GetAddressBytes();
        byte[] networkOctets = subnet.GetAddressBytes();

        // ensure that IPv4 isn't mixed with IPv6
        if (addressOctets.Length != subnetOctets.Length
            || addressOctets.Length != networkOctets.Length)
        {
            return false;
        }

        for (int i = 0; i < addressOctets.Length; i += 1)
        {
            var addressOctet = addressOctets[i];
            var subnetOctet = subnetOctets[i];
            var networkOctet = networkOctets[i];

            if (networkOctet != (addressOctet & subnetOctet))
            {
                return false;
            }
        }
        return true;
    }
}

Example usage:

用法示例:

var subnetAndMask = IpAddresses.GetSubnetAndMaskFromCidr("10.132.0.0/20");
bool result = IpAddresses.IsAddressOnSubnet(
    IPAddress.Parse("10.132.12.34"),
    subnetAndMask.Item1,
    subnetAndMask.Item2);

回答by joelc

I'm late to the party here, but had a similar need, and put together a quick package to do exactly this.

我在这里参加聚会迟到了,但有类似的需求,并整理了一个快速包来做到这一点。

https://www.nuget.org/packages/IpMatcher/

https://www.nuget.org/packages/IpMatcher/

and source:

和来源:

https://github.com/jchristn/IpMatcher

https://github.com/jchristn/IpMatcher

Simple use:

简单使用:

using IpMatcher;

Matcher matcher = new Matcher();
matcher.Add("192.168.1.0", "255.255.255.0");
matcher.Add("192.168.2.0", "255.255.255.0");
matcher.Remove("192.168.2.0");
matcher.Exists("192.168.1.0", "255.255.255.0");  // true
matcher.Match("192.168.1.34"); // true
matcher.Match("10.10.10.10");  // false

回答by Neetin

I have also created a class which calculates the network and broadcast address and checks if the IP is neither broadcast nor network address.

我还创建了一个类来计算网络和广播地址,并检查 IP 是否既不是广播地址也不是网络地址。

private static IPValidationFailedReason PerformIPRangeValidation(string ipAddress, string subnetMask)
        {
            IPValidationFailedReason ipValidationType = IPValidationFailedReason.None;
            string networkaddress = string.Empty;
            string broadcastAddress = string.Empty;
            string networkAddressBinary = string.Empty;
            string broadcastAddressBinary = string.Empty;
            int zerosCountInSubnetMask = 0;

            Array.ForEach(subnetMask.Split(SplitterChar), (eachOctet) => Array.ForEach(IPInterfaceHelper.GetOctetWithPadding(eachOctet).Where(c => c == CharZero).ToArray(), (k) => zerosCountInSubnetMask++));

            if (zerosCountInSubnetMask == 0)
            {
                return ipValidationType;
            }

            string ipAddressBinary = IPInterfaceHelper.ToBinary(ipAddress);
            networkAddressBinary = GetNetworkAddressInBinaryFormat(zerosCountInSubnetMask, ipAddressBinary);
            broadcastAddressBinary = GetBroadcastAddressInBinaryFormat(zerosCountInSubnetMask, ipAddressBinary);

            networkaddress = ToIPFromBinary(networkAddressBinary);
            broadcastAddress = ToIPFromBinary(broadcastAddressBinary);

            if (ipAddress == networkaddress)
            {
                ipValidationType = IPValidationFailedReason.NetworkAddressZero;
                return ipValidationType;
            }
            if (ipAddress == broadcastAddress)
            {
                ipValidationType = IPValidationFailedReason.BroadcastAddressNotPermiited;
                return ipValidationType;
            }

            return ipValidationType;
        }


private static string GetNetworkAddressInBinaryFormat(int zeroCountInSubnetMask, string ipAddressBinary)
    {
        string networkAddressBinary = string.Empty;
        int countOfOnesInSubnetMask = TotalBitCount - zeroCountInSubnetMask;
        StringBuilder sb = new StringBuilder(ipAddressBinary);
        //When Subnet is like 255.255.255.0
        if (zeroCountInSubnetMask >= 1 && zeroCountInSubnetMask <= 8)
        {
            networkAddressBinary = sb.Replace(CharOne, CharZero, countOfOnesInSubnetMask + 3, zeroCountInSubnetMask).ToString();
        }
        //When Subnet is like 255.255.0.0
        if (zeroCountInSubnetMask > 8 && zeroCountInSubnetMask <= 16)
        {
            networkAddressBinary = sb.Replace(CharOne, CharZero, countOfOnesInSubnetMask + 2, zeroCountInSubnetMask + 1).ToString();
        }
        //When Subnet is like 255.0.0.0
        if (zeroCountInSubnetMask > 16 && zeroCountInSubnetMask <= 24)
        {
            networkAddressBinary = sb.Replace(CharOne, CharZero, countOfOnesInSubnetMask + 1, zeroCountInSubnetMask + 2).ToString();
        }
        //When Subnet is like 128.0.0.0
        if (zeroCountInSubnetMask > 24 && zeroCountInSubnetMask < 32)
        {
            networkAddressBinary = sb.Replace(CharOne, CharZero, countOfOnesInSubnetMask , zeroCountInSubnetMask + 3).ToString();
        }
        return networkAddressBinary;
    }


 private static string GetBroadcastAddressInBinaryFormat(int zeroCountInSubnetMask, string ipAddressBinary)
    {
        string broadcastAddressBinary = string.Empty;
        int countOfOnesInSubnetMask = TotalBitCount - zeroCountInSubnetMask;
        StringBuilder sb = new StringBuilder(ipAddressBinary);
        //When Subnet is like 255.255.255.0
        if (zeroCountInSubnetMask >= 1 && zeroCountInSubnetMask <= 8)
        {
            broadcastAddressBinary = sb.Replace(CharZero, CharOne, countOfOnesInSubnetMask + 3, zeroCountInSubnetMask).ToString();
        }
        //When Subnet is like 255.255.0.0
        if (zeroCountInSubnetMask > 8 && zeroCountInSubnetMask <= 16)
        {
            broadcastAddressBinary = sb.Replace(CharZero, CharOne, countOfOnesInSubnetMask + 2, zeroCountInSubnetMask + 1).ToString();
        }
        //When Subnet is like 255.0.0.0
        if (zeroCountInSubnetMask > 16 && zeroCountInSubnetMask <= 24)
        {
            broadcastAddressBinary = sb.Replace(CharZero, CharOne, countOfOnesInSubnetMask + 1, zeroCountInSubnetMask + 2).ToString();
        }
        //When Subnet is like 128.0.0.0
        if (zeroCountInSubnetMask > 24 && zeroCountInSubnetMask < 32)
        {
            broadcastAddressBinary = sb.Replace(CharZero, CharOne, countOfOnesInSubnetMask , zeroCountInSubnetMask + 3).ToString();
        }
        return broadcastAddressBinary;
    }

private static string ToIPFromBinary(string ipAddressBinary)
        {
            string addrTemp = string.Empty;
            string[] networkAddressBinaryOctets = ipAddressBinary.Split(SplitterChar);
            foreach (var eachOctet in networkAddressBinaryOctets)
            {
                string temp = Convert.ToUInt32(eachOctet, 2).ToString(CultureInfo.InvariantCulture);
                addrTemp += temp + SplitterChar;
            }
            // remove last '.'
            string ipAddress = addrTemp.Substring(0, addrTemp.Length - 1);
            return ipAddress;
        }