【问题标题】:How to see if an IP address belongs inside of a range of IPs using CIDR notation?如何使用 CIDR 表示法查看 IP 地址是否属于 IP 范围内?
【发布时间】:2012-03-26 05:59:00
【问题描述】:

这里我有一个静态引用我需要检查的范围:

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

关于如何编写此功能的任何建议?

【问题讨论】:

  • 您是在要求我们完成这项工作吗?
  • IPNetwork 项目应该能够处理您想要做的大部分事情。
  • @Tony:向正确方向轻推同样好。这就是这个网站的重点。 :) 我已经在 Ruby 中完成了这项工作,但我不太熟悉如何在 C# 中完成这项工作。
  • 那么,你是如何在 Ruby 中解决这个问题的呢?其中哪一部分在翻译成 C# 时遇到问题?
  • @M.Babcock IPNetwork 项目已移至github.com/lduchosal/ipnetwork。我想我已经更改了此处答案中的所有参考资料,但如果您记得在其他地方链接到它,您可能也想更新这些参考资料,如果您愿意的话。

标签: c# ip cidr


【解决方案1】:

如果您了解 CIDR 表示法,则可以轻松地在 parse 方法中进行数学运算。

您基本上知道 IPv4 地址是 32 位长,CIDR 表示法意味着“/”后面的位数是网络地址位(即屏蔽位),因此剩余位表示数字子网中的主机数。

来自维基百科article

掩码或前缀定义的子网地址数量可以 计算为 2address size - 前缀大小,其中地址 IPv6 的大小为 128,IPv4 的大小为 32。例如,在 IPv4 中,前缀 /29 的大小给出:232-29 = 23 = 8 个地址。

所以您可以(不,我不会为您计算详细信息)将您的地址转换为二进制并使用给定的掩码(也是二进制形式)执行 binary AND,然后您还剩下 IP 地址的网络地址部分,它应该与您要检查的任何地址匹配以查看它是否在特定子网中。

【讨论】:

  • 您说“不,我不会为您解决细节”这一事实表明这可能是在重新发明轮子(此类日常问题的耗时任务会自动快速完成) )。
【解决方案2】:

幸运的是,大部分工作已经为您完成(所以我们不必这样做)。查看IPNetwork 项目。您将使用 IPNetwork.Parse 解析所有 CIDR 地址。然后查看特定的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

当然还有很多事情可以(并且可能应该)用它来做,但这证明了这个概念。

【讨论】:

  • Can't get IPNetwork to run in a VB.Net project... Intellisense 喜欢它,但运行时出错,想找到 IPNetwork.cs! :( 可能仅限于 C#?
  • @PatTrainor - 请打开一个新问题,以便社区的相应成员可以查看您的情况。我可以向你保证,不可能限制能够使用程序集的语言。
  • 我支持你!我见过的最危险的事情...将创建新线程... [也没办法问作者...]
  • @M.Babcock。请您解释一下为什么使用字典而不是列表?
  • @fravelgue - 恰好是 3 年前写答案时我想到的。可以说,可以对提供的代码进行几项性能改进,而字典是有保证的。
【解决方案3】:

首先,你应该使用它:

IPNetwork ipnetwork = IPNetwork.Parse("192.168.168.100/29");
Console.WriteLine("CIDR: {0}", ipnetwork.Cidr);

输出

CIDR: 29

【讨论】:

【解决方案4】:

决定回答我自己的问题,以便人们受益。如果可以改进,请做!

我使用了IPNetwork library,效果非常好!下面是我使用的代码:

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

【讨论】:

  • IpIsWithinBoliviaRange 方法的大部分内容(几乎是 foreach)可以用一个简单的 LINQ 查询替换:return Bolivia_Ip_Range.Any(subnet =&gt; IPNetwork.Contains(IPNetwork.Parse(subnet), incomingIp));
  • 第一印象,这个解决方案看起来效率很低。每次调用该方法时,您都在列表中的每个子网上执行 IPNetwork network = IPNetwork.Parse(subnet) !!!您知道它总是会解析为相同的结果,那么为什么不记住它呢?同样,您也可以(取决于您可能会收到多少重复 IP 访问者)根据传入 IP 地址记忆最终结果以保存查找 - 尽管如果预期有大量不同的 IP 地址,这实际上可能不是一个好主意低重复。
【解决方案5】:

如果您不想/不能将另一个库(作为 IPnetwork 库)添加到您的项目并且只需要处理 IPv4 CIDR 范围,这里有一个快速解决您问题的方法

// 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(ipAddress).GetAddressBytes(), 0);
    int CIDR_addr = BitConverter.ToInt32(IPAddress.Parse(parts[0]).GetAddressBytes(), 0);
    int CIDR_mask = IPAddress.HostToNetworkOrder(-1 << (32 - int.Parse(parts[1])));

    return ((IP_addr & CIDR_mask) == (CIDR_addr & CIDR_mask));
}

以上将允许您快速检查给定的 IPv4 地址是否在给定的 CIDR 范围内;请注意,上面的代码是准系统,在将它们提供给函数之前,您可以检查给定的 IP(字符串)和 CIDR 范围是否正确(您可以只使用 tryparse 或其他...)

【讨论】:

  • 它不会真正影响结果,但您可能应该将解析的值交换为 IP_addr 和 CIDR_addr。
  • 这没有通过 CIDR 范围 0.0.0.0/0 换句话说 all。除此之外,它很棒!
  • 如果 CIDR 掩码为零,我只需添加一个 if 以返回 true,因为任何有效的都将匹配。
  • IP_addr 和 CIDR_addr 变量按照 Jeffrey 的建议进行了交换。
  • 有人能解释一下这一行吗:IPAddress.HostToNetworkOrder(-1
【解决方案6】:

我使用以下方法来确定给定 IP 地址是公共 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);
    }

【讨论】:

    【解决方案7】:
        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;
        }
    

    【讨论】:

      猜你喜欢
      • 2013-08-22
      • 2012-11-07
      • 2017-05-21
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-14
      • 2023-04-05
      相关资源
      最近更新 更多