Как узнать, принадлежит ли IP-адрес внутри диапазона IP-адресов с использованием нотации CIDR?
Здесь у меня есть статическая ссылка на диапазоны, которые мне нужно проверить:
private static List<string> Ip_Range = new List<string>()
{
"12.144.86.0/23",
"31.201.1.176/30",
"46.36.198.101/32",
"46.36.198.102/31",
"46.36.198.104/31",
"46.136.172.0/24",
"63.65.11.0/24",
"63.65.12.0/25",
"63.65.12.128/26",
"63.65.12.192/27",
"63.65.12.224/28",
"63.65.12.240/29",
"63.65.12.248/30",
"63.65.12.252/31",
"63.65.12.254/32",
"65.173.56.0/21",
"67.23.241.179/32",
"67.23.241.180/30",
"67.23.241.184/29",
"67.23.241.192/30",
"67.23.241.196/31",
"67.23.241.198/32",
"72.32.164.56/29",
"72.46.244.32/28",
"74.91.16.48/29",
"74.91.16.208/29",
"74.91.20.48/28",
"74.91.20.64/29",
"74.112.134.120/29",
"74.112.135.104/29",
"74.205.37.16/29",
"78.24.205.32/28",
"98.129.27.88/29",
"98.129.91.40/29",
"166.114.0.0/16",
"167.157.0.0/16",
"174.143.165.80/29",
"186.0.156.0/22",
"186.2.0.0/17",
"186.27.0.0/17",
"190.0.248.0/21",
"190.3.184.0/21"
};
Вот некоторый псевдокод о том, как я вижу это работает:
public static bool IpIsWithinRange(string ip) //Something like 127.0.0.1 or 184.56.26.35
{
IPAddress incomingIp = IPAddress.Parse(ip);
foreach (var subnet in Ip_Range)
{
IPAddress sub = IPAddress.Parse(subnet); ?????
if (incomingIp "is in" sub) ?
return true;
}
return false;
}
Любые предложения о том, как кодировать эту функциональность?
7 ответов
Решил ответить на свой вопрос, чтобы люди могли получить пользу. Если это может быть улучшено, пожалуйста, сделайте!
Я использовал библиотеку IPNetwork, и это сработало фантастически! Ниже приведен код, который я использовал:
using System.Net;
public static class RedirectHelpers
{
public static bool IpIsWithinBoliviaRange(string ip)
{
IPAddress incomingIp = IPAddress.Parse(ip);
foreach (var subnet in Bolivia_Ip_Range)
{
IPNetwork network = IPNetwork.Parse(subnet);
if (IPNetwork.Contains(network, incomingIp))
return true;
}
return false;
}
private static List<string> Bolivia_Ip_Range = new List<string>()
{
"12.144.86.0/23",
"31.201.1.176/30",
"46.36.198.101/32",
"46.36.198.102/31",
"46.36.198.104/31",
"46.136.172.0/24",
"63.65.11.0/24",
"63.65.12.0/25",
"63.65.12.128/26",
"63.65.12.192/27",
"63.65.12.224/28",
"63.65.12.240/29",
"63.65.12.248/30",
"63.65.12.252/31",
"63.65.12.254/32",
"65.173.56.0/21",
"67.23.241.179/32",
"67.23.241.180/30",
"67.23.241.184/29",
"67.23.241.192/30",
"67.23.241.196/31",
"67.23.241.198/32",
"72.32.164.56/29",
"72.46.244.32/28",
"74.91.16.48/29",
"74.91.16.208/29",
"74.91.20.48/28",
"74.91.20.64/29",
"74.112.134.120/29",
"74.112.135.104/29",
"74.205.37.16/29",
"78.24.205.32/28",
"98.129.27.88/29",
"98.129.91.40/29",
"166.114.0.0/16",
"167.157.0.0/16",
"174.143.165.80/29",
"186.0.156.0/22",
"186.2.0.0/17",
"186.27.0.0/17",
"190.0.248.0/21",
"190.3.184.0/21",
"166.114.0.0/16",
"167.157.0.0/16",
"186.2.0.0/18",
"190.11.64.0/20",
"190.11.80.0/20",
"190.103.64.0/20",
"190.104.0.0/19",
"190.107.32.0/20",
"190.129.0.0/17",
"190.181.0.0/18",
"190.186.0.0/18",
"190.186.64.0/18",
"190.186.128.0/18",
"200.7.160.0/20",
"200.58.64.0/20",
"200.58.80.0/20",
"200.58.160.0/20",
"200.58.176.0/20",
"200.75.160.0/20",
"200.85.128.0/20",
"200.87.0.0/17",
"200.87.128.0/17",
"200.105.128.0/19",
"200.105.160.0/19",
"200.105.192.0/19",
"200.112.192.0/20",
"200.119.192.0/20",
"200.119.208.0/20",
"201.222.64.0/19",
"201.222.96.0/19"
};
}
Если вы не хотите / не можете добавить другую библиотеку (например, IP-сеть) в свой проект и вам просто нужно иметь дело с диапазонами CIDR IPv4, вот быстрое решение вашей проблемы
// true if ipAddress falls inside the CIDR range, example
// bool result = IsInRange("10.50.30.7", "10.0.0.0/8");
private bool IsInRange(string ipAddress, string CIDRmask)
{
string[] parts = CIDRmask.Split('/');
int IP_addr = BitConverter.ToInt32(IPAddress.Parse(parts[0]).GetAddressBytes(), 0);
int CIDR_addr = BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0);
int CIDR_mask = IPAddress.HostToNetworkOrder(-1 << (32 - int.Parse(parts[1])));
return ((IP_addr & CIDR_mask) == (CIDR_addr & CIDR_mask));
}
вышеупомянутое позволит вам быстро проверить, попадает ли данный IPv4-адрес в заданный диапазон CIDR; обратите внимание, что приведенный выше код является barebone, вам нужно проверить правильность заданного IP (строки) и диапазона CIDR перед передачей их в функцию (вы можете просто использовать tryparse или любой другой...)
К счастью, большая часть работы уже была проделана для вас (поэтому нам не нужно). Проверьте проект IPNetwork. Вы проанализируете все свои адреса CIDR с IPNetwork.Parse
, Затем, чтобы увидеть, если конкретный IPAddress
в диапазоне просто использовать IPNetwork.Contains
метод.
Мне стало скучно, поэтому вот метод, который вы можете использовать, чтобы проверить, находится ли IP-адрес в диапазоне или нет:
private Dictionary<string, IPNetwork> netCache = null;
public bool IsInRange(string ipAddress)
{
if (netCache == null)
netCache = Ip_Range.ToDictionary((keyItem) => keyItem, (valueItem) => IPNetwork.Parse(valueItem));
return netCache.Values.Any(net => IPNetwork.Contains(net, IPAddress.Parse(ipAddress)));
}
Это зависит от Ip_Range
список из вашего вопроса, но переводит их IPNetwork
экземпляры (пропущенные проверки здравомыслия для краткости).
Использование:
List<string> addrList = new List<string> { "12.144.86.1", "10.254.6.172" };
addrList.ForEach(addr => Console.WriteLine("{0}: {1}", addr, IsInRange(addr)));
Тестовый вывод:
12.144.86.1: True
10.254.6.172: False
Конечно, еще многое можно (и, вероятно, следует) сделать с этим, но это подтверждает концепцию.
public static bool IpIsInRange(string subnet, string ip)
{
var splitSubnet = subnet.Split('/');
var maskBits = 32 - int.Parse(splitSubnet[1]);
if (maskBits == 32)
{
return true;
}
var subnetIp = BitConverter.ToInt32(IPAddress.Parse(splitSubnet[0]).GetAddressBytes().Reverse().ToArray(), 0) >> maskBits << maskBits;
var clientIp = BitConverter.ToInt32(IPAddress.Parse(ip).GetAddressBytes().Reverse().ToArray(), 0) >> maskBits << maskBits;
return subnetIp == clientIp;
}
Если вы понимаете нотацию CIDR, вы можете легко сделать математику в вашем методе разбора.
Вы в основном знаете, что адрес IPv4 имеет длину 32 бита и что нотация CIDR означает, что число битов после "/" является битами сетевого адреса (то есть битами с маскировкой), поэтому оставшиеся биты представляют количество хостов в подсеть.
Из статьи википедии:
Количество адресов подсети, определяемых маской или префиксом, можно рассчитать как 2адресный размер - размер префикса, в котором размер адреса для IPv6 составляет 128, а для IPv4 - 32. Например, в IPv4 размер префикса /29 дает: 232-29 = 23 = 8 адресов.
Таким образом, вы можете (нет, я не собираюсь прорабатывать детали для вас) преобразовать ваши адреса в двоичный файл и выполнить двоичный файл И с заданной маской (также в двоичном виде), тогда у вас есть часть сетевого адреса IP-адреса left, который должен совпадать с любым адресом, по которому вы проверяете, чтобы увидеть, находится ли он в определенной подсети.
Для начала вы должны использовать это:
IPNetwork ipnetwork = IPNetwork.Parse("192.168.168.100/29");
Console.WriteLine("CIDR: {0}", ipnetwork.Cidr);
Выход
CIDR: 29
Я использую метод ниже, чтобы определить, является ли данный IP-адрес общедоступным или частным / внутренним:
private bool IsInternalIpAddress(string ipAddress)
{
// notes: http://whatismyipaddress.com/private-ip
var internalIpRanges = Enumerable
.Range(16, 31)
.Select(i => "172.{0}.".FormatWith(i))
.Concat(new[] {"192.168.", "10.", "127."})
.ToArray();
return ipAddress.StartsWith(internalIpRanges);
}