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

问题描述 投票:0回答:9

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

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

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

c# ip-address subnet
9个回答
41
投票

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

35
投票

使用 ThomasChris 的答案以及 Ciscos 子网划分示例,如果您使用 CIDR 表示法(IPAddress/PrefixLength),我终于得到了适用于 IPv4 和 IPv6 的东西。我的 IPv6 实现可能有点太直接了,但由于没有 UInt128 数据类型,我无法适应 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 (maskLength == 0) { return true; } if (maskLength < 0) { throw new NotSupportedException("A Subnetmask should not be less than 0."); } 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. Reverse the BitArray to compare the bits of each byte in the right order. var maskAddressBits = new BitArray(maskAddress.GetAddressBytes().Reverse().ToArray()); // And convert the IpAddress to a BitArray. Reverse the BitArray to compare the bits of each byte in the right order. var ipAddressBits = new BitArray(address.GetAddressBytes().Reverse().ToArray()); var ipAddressLength = ipAddressBits.Length; if (maskAddressBits.Length != ipAddressBits.Length) { throw new ArgumentException("Length of IP Address and Subnet Mask do not match."); } // Compare the prefix bits. for (var i = ipAddressLength - 1; i >= ipAddressLength - maskLength; i--) { if (ipAddressBits[i] != maskAddressBits[i]) { return false; } } return true; } throw new NotSupportedException("Only InterNetworkV6 or InterNetwork address families are supported."); }
这是我测试它的 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")] [InlineData("192.168.5.85/0", "0.0.0.0")] [InlineData("192.168.5.85/0", "255.255.255.255")] 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")] [InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:5000:0000:0000:0000:0000")] [InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:57ff:ffff:ffff:ffff:ffff")] [InlineData("2001:db8:abcd:0012::0/0", "::")] [InlineData("2001:db8:abcd:0012::0/0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")] 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")] [InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:4999:0000:0000:0000:0000")] [InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:5800:0000:0000:0000:0000")] public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) { var ipAddressObj = IPAddress.Parse(ipAddress); Assert.False(ipAddressObj.IsInSubnet(netMask)); } }
作为测试的基础,我使用了 

Ciscos 子网划分示例IBMs IPV6 地址示例

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


20
投票
位操作有效。将 IP 填充到 32 位无符号整数中,对子网地址、

&

掩码都与 
0xFFFFFFFF << (32-20)
 执行相同操作并进行比较:

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

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

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

这适用于 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; } }

使用示例:

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

3
投票
如果您使用 ASP.NET Core,有一个新类

IPNetwork

 可用于测试 IP 地址是否在特定网络中。

例如,以下代码片段检查

192.168.1.1

 是否是 
192.168.1.0/6
 的一部分

IPNetwork ipnetwork = new IPNetwork(IPAddress.Parse("192.168.1.0"), 6); bool isPartOfTheSubnet = ipnetwork.Contains(IPAddress.Parse("192.168.1.1"));
    

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

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

来源:

https://github.com/jchristn/IpMatcher

使用简单:

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
    

2
投票
解决方案是使用

System.Net.IPAddress

 将 IP 地址转换为字节,并对地址、子网和掩码八位字节进行按位比较。

二元 AND 运算符

&

 如果两个操作数中都存在该结果,则将其复制一点到结果中。

代码:

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

特别感谢

参考


0
投票
我还创建了一个类来计算网络和广播地址,并检查 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; }
    

0
投票
对 Chris Shouts 回答要求 IPV6 支持的评论进行一点更正:

public static IPAddress GetNetworkAddress(this IPAddress address, IPAddress subnetMask) { byte[] ipAdressBytes = address.GetAddressBytes(); byte[] subnetMaskBytes = subnetMask.GetAddressBytes(); if (ipAdressBytes.Length != subnetMaskBytes.Length) subnetMaskBytes = subnetMask.MapToIPv6().GetAddressBytes(); 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); }
    
© www.soinside.com 2019 - 2024. All rights reserved.