У меня есть подсеть в формате 10.132.0.0/20 и IP-адрес из объекта запроса ASP.Net.
Есть ли функция .NET framework для проверки наличия IP-адреса в данной подсети?
Если нет, как это можно сделать? Бит-манипуляция, я думаю?
У меня есть подсеть в формате 10.132.0.0/20 и IP-адрес из объекта запроса ASP.Net.
Есть ли функция .NET framework для проверки наличия IP-адреса в данной подсети?
Если нет, как это можно сделать? Бит-манипуляция, я думаю?
Используя ответы Томаса и Криса вместе с примерами подсетей Ciscos, я, наконец, получил кое-что для IPv4 и IPv6, если вы используете нотацию CIDR (IPAddress/PrefixLength). Моя реализация IPv6 может быть слишком прямой, но, поскольку нет UInt128-типа данных, я не смог адаптировать решение Thomas. Вот код, который, кажется, работает хорошо:
public static bool IsInSubnet(this IPAddress address, string subnetMask)
{
var slashIdx = subnetMask.IndexOf("/");
if (!subnetMask.Contains("/"))
{ // 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());
// And convert the IpAddress to an unsigned integer.
var ipAdressBits = BitConverter.ToUInt32(address.GetAddressBytes().Reverse().ToArray());
// Get the mask/network address as unsigned integer.
uint mask = uint.MaxValue << (32 - maskLength);
// /questions/197001/how-to-check-if-an-ip-address-is-within-a-particular-subnet/1078462#1078462
// 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) == (ipAdressBits & 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 ipAdressBits = new BitArray(address.GetAddressBytes());
if (maskAddressBits.Length != ipAdressBits.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 (ipAdressBits[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")]
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));
}
}
В качестве базы для тестов я использовал Примеры подсетей Ciscos и примеры адресов IPv6 от IBM.
Я надеюсь, что кто-то найдет это полезным;)
Посмотрите расчеты IP-адресов с помощью С# в блогах MSDN. Он содержит метод расширения (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);
}
}
Работа с битами. Поместите 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)) {
// ...
}
Поскольку код блога 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);
Я опоздал на вечеринку здесь, но у меня была похожая потребность, и я собрал быстрый пакет, чтобы сделать именно это.
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
Решение состоит в том, чтобы преобразовать IP-адрес в байты с помощью System.Net.IPAddress
и выполнить побитовые сравнения для адресов, подсетей и октетов маски.
Двоичный оператор 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;
}
}
Особая благодарность Ссылка