【问题标题】:Matching an ip address to a table of subnet masks... What is the best approach in Java?将 IP 地址与子网掩码表匹配... Java 中最好的方法是什么?
【发布时间】:2010-02-16 15:17:36
【问题描述】:

这是场景...我有一个子网表。 (见下文)我有一个 IP 地址。我想根据表中的查找找出 ip 地址属于哪个子网。然后,该关联将用于确定用户所在的位置。这是一个私有网络空间,因此标准的互联网到位置查找将不适用。最好的方法是什么?我是否需要将 IP 地址分解为数字部分并与所有子网进行按位比较。还是 Java API 中的内置工具可以让我更轻松地将 IP 地址与子网掩码进行比较?

我主要是在寻找将 ipaddress 与给定子网掩码进行比较并确定是匹配的最佳方法,或者不匹配。可选。任何有关如何以最少的操作存储列表和搜索的提示也将不胜感激。

理想情况下,我会做类似的事情:

List subnetInfo = null;

subnetInfo = findSubnet('192.168.0.1');  //value null if nothing found

....

//return null if nothing found
List findSubnet(String ipaddress) {
   List subnetDetails = null;
   .... code here ...
   return subnetDetails;
}

表 1:子网示例列表

dk-ballerup-gen-off-v411        10.172.80.0/21  NANR-denmark-ballerup-metallbuen66-ground-first-floors-incl-dhcp-(sr14585203)
ae-dubai-ofssl-gen-off-v410  10.172.88.0/24  NANR-arab-emirates-ofssl-iflex-general-office-v410-(sr12781477)
ru-stpetersburg-gen-off-v411    10.172.89.0/24  NANR-russia-stpetersburg-general-office-incl-dhcp (bsteinba)

【问题讨论】:

    标签: java network-programming


    【解决方案1】:

    Apache Commons Net 有一个 SubnetUtils 用于此类事情,包括确定给定 IP 地址是否在给定子网内。

    简单的例子:

        String[] subnetsMasks = { ... };
        Collection<SubnetInfo> subnets = new ArrayList<SubnetInfo>();
        for (String subnetMask : subnetsMasks) {
            subnets.add(new SubnetUtils(subnetMask).getInfo());
        }
    
        String ipAddress = ...;
        for (SubnetInfo subnet : subnets) {
            if (subnet.isInRange(ipAddress)) {
                System.out.println("IP Address " + ipAddress + " is in range " + subnet.getCidrSignature());
            }
        }
    

    【讨论】:

    • 为什么你需要一个 API 来处理你可以用 & 运算符检查的东西? ...这就是为什么网络掩码被称为网络掩码...
    • 因为将 CIDR 掩码转换为数字是您很容易出错的任务。为什么要重新实施它,什么时候可以下架?
    • 你在开玩笑吧?您甚至不必将字节转换为整数,您只需在循环中屏蔽字节即可。大约 4 行代码。
    • 如果 CIDR 掩码跨越字节边界?更多的代码行。还有更多的错误。这个问题已经在图书馆解决了,还有更好的事情可以花我们的精力。
    【解决方案2】:

    请记住,由于历史原因,IP 地址只是一个 int 值,以十进制形式表示为 4 个八位字节。

    出于同样的原因,子网实际上是从网络地址到广播地址的连续ints 范围。

    因此,如果您的 IP 地址对象有一个 int 转换器,您可以通过简单的 int 比较来检查该整数是否在子网范围内。

    【讨论】:

      【解决方案3】:

      执行此操作的最佳方法是使用地址树并同时检查所有地址中的包含情况。 The IPAddress Java library 为 IPv4 和 IPv6 提供了执行此操作的方法。免责声明:我是项目经理。

      String subnets[] = { "10.172.80.0/21", "10.172.88.0/24", "10.172.89.0/24" };
      IPv4AddressTrie blocksTrie = new IPv4AddressTrie();
      for(String str : subnets) {
          blocksTrie.add(new IPAddressString(str).getAddress().toIPv4());
      }
      IPAddressString toFindAddrStr = new IPAddressString("10.172.88.3");
      IPv4Address toFindBlockFor = toFindAddrStr.getAddress().toIPv4();
      IPv4TrieNode containingNode = blocksTrie.elementsContaining(toFindBlockFor);
      System.out.println("For address " + toFindBlockFor + " containing block is " +
          containingNode.toTreeString(false, false) + "\n");
      

      输出:

      For address 10.172.88.3 containing block is 
      ● 10.172.88.0/24
      

      地址还提供了逐一检查收容措施的方法,如以下答案:How to check if an IP address is from a particular network/netmask in Java?

      【讨论】:

        【解决方案4】:

        如果您的表是 SQL 表并且您的数据库是 PostgreSql,则可以使用 PostgreSQL typesfunctions

        嗯,有很多条件......

        【讨论】:

          【解决方案5】:

          将 IP 地址和网络掩码转换为整数并将它们组合在一起。如果结果等于 IP 地址,则它是该网络掩码中的地址。请注意,如果您的网络掩码不同,您可能会得到多个匹配项。

          【讨论】:

          • 这仅适用于 IPv4。同样的技术适用于 IPv6,但地址不是整数。他们甚至不长。它们是 128 位数字,Java 将其表示为字节数组。
          【解决方案6】:

          这是一个老问题,但我认为值得拯救别人它给我带来的痛苦。

          避开 Apache Commons Net SubnetUtils 类,它有问题:

          http://en.newinstance.it/2009/03/25/checking-if-an-ip-is-in-a-subnet-range-subnetutilssubnetinfo-isinrange-bug/

          【讨论】:

          • 您的链接已损坏,请您更新一下吗? Apache Commons 出了什么问题?
          • 好问题。现在6岁了,不知道具体是什么。我怀疑我的情况明显不正确,查看了代码并发现了一个错误。鉴于自己敷面膜很容易,我会这样做。
          • 如果这么简单,Apache 怎么弄错了? ;-) 看起来spring-web-security 中的 IpAddressMatcher 现在可以做到这一点。
          • 对。我回头看,模糊的记忆与我发现的相符。如果您查看:issues.apache.org/jira/browse/NET-260,您会发现他们没有考虑到整数可能是负数,因此他们没有正确检查范围。看起来他们在我遇到它的时候已经在 SVN 中修复了它,我当时没有发现,但我很确定没有发布版本。当然我找不到没有这个错误的版本。
          • 所以,平心而论,他们现在已经解决了这个问题,如果你现在想使用它,请把自己淘汰。
          【解决方案7】:

          我面临与您相同的问题,但仅限于private networks192.168.X.X, 10.x.x.x, ... 和 IPv6)。

          如果你也受限于这种情况,你应该使用内置的InetAddress.isSiteLocalAddress()

          【讨论】:

            【解决方案8】:

            我在 skaffman 解决方案的基础上构建了一种方法。对于那些对复制粘贴解决方案感兴趣的人:

                /**
                 * Checks if client ip equals or is in range of ip networks provided by
                 * semicolon separated list
                 *
                 * @param clientIp in numeric form like "192.168.0.10"
                 * @param ipList   like "127.0.0.1;192.168.0.0/24;10.0.0.0/8"
                 * @return true if client ip from the list os ips and networks
                 */
                protected boolean evaluateIp(String clientIp, String ipList) {
                    BusinessLogger log = getLog();
                    log.debug("Evaluate ip ");
                    if (ipList == null) {
                        log.info("No ip address provided in ApiPrivilege");
                        return false;
                    }
                    String[] subnetMasks = ipList.split(";");
                    for (String subnetMask : subnetMasks) {
                        subnetMask = subnetMask.trim();
                        if (eq(subnetMask, clientIp))
                            return true;
                        if (subnetMask.contains("/")) {
                            if (new SubnetUtils(subnetMask).getInfo().isInRange(clientIp))
                                return true;
                        }
                    }
                    return false;
                }
            

            【讨论】:

              【解决方案9】:

              在 pom.xml 中包含以下 jar

              *<dependency>
                  <groupId>org.springframework.security</groupId>
                  <artifactId>spring-security-web</artifactId>
                  <version>3.0.7.RELEASE</version>
              </dependency>*
              

              Spring提供了上述库来支持ipv4和ipv6。

              下面是检查 ip 是否属于给定子网的代码 sn-p。

              System.out.println(matches("192.168.2.1", "192.168.2.1/11"));
              System.out.println(  matches("192.168.2.1", "192.168.2.0/32")); 
              System.out.println(  matches("192.168.2.5", "192.168.2.0/24"));
              System.out.println(  matches("fe80:0:0:0:0:0:c0a8:11", 
                "fe80:0:0:0:0:0:c0a8:1/120")); 
              System.out.println(  matches("fe80:0:0:0:0:0:c0a8:11", 
                "fe80:0:0:0:0:0:c0a8:1/128")); 
              System.out.println( matches("2401:4900:1780:0:0:0:0:11", 
                 "2401:4900:1780:0:0:0:0:0/44")); 
              
              
              private static boolean matches(String ip, String subnet) {
                  IpAddressMatcher ipAddressMatcher = new IpAddressMatcher(subnet);
                  return ipAddressMatcher.matches(ip);
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-08-16
                • 1970-01-01
                • 2016-09-12
                • 2016-06-16
                • 2015-10-18
                • 2013-08-24
                • 2012-07-19
                • 2021-12-30
                相关资源
                最近更新 更多