【问题标题】:How to calculate end IP in the range, based on start IP and network mask length如何根据起始 IP 和网络掩码长度计算范围内的结束 IP
【发布时间】:2014-07-19 00:11:45
【问题描述】:

我有一个起始 IPv4 IP 地址5.39.28.128(或::ffff:5.39.28.128),我有一个 IPv6 网络掩码长度122,如何计算该范围内的最后一个 IP?

我认为我需要将起始 IP 转换为二进制,如下所示,我不知道从那里去哪里才能得到最终 IP。

$ipNumber = ip2long('5.39.28.128');
$ipBinary = decbin($ipNumber);

echo $ipBinary; // 101001001110001110010000000

原因是我将 CSV 格式的 MaxMind GeoIP 数据库导入 MySQL 数据库(因此如果需要,可以使用 MySQL 函数)。 MaxMind 不再提供结束 IP,而是提供起始 IP 和 IPv6 网络掩码长度。

【问题讨论】:

  • @TrippKinetics 不是那个的副本。我问的是 PHP 而不是 C,我没有完整的网络掩码,只有长度。
  • #1,即使语言不同,逻辑上也没有区别。 #2,网络掩码和长度是等价的。 en.wikipedia.org/wiki/Subnetwork
  • @TrippKinetics 你是对的,你的链接很有价值,这个问题不是重复的。重复的问题是真正的重复,语言的差异使它不是重复的。
  • 您想在 php 或 MySQL 中执行此操作吗?如果您对 MySQL 不感兴趣,请从问题中删除标签。

标签: php mysql network-programming ip maxmind


【解决方案1】:

给你。我从this response to another question 复制了inet_to_bits 函数。

<?php

function inet_to_bits($inet) {
   $inet = inet_pton($inet);
   $unpacked = unpack('A16', $inet);
   $unpacked = str_split($unpacked[1]);
   $binaryip = '';
   foreach ($unpacked as $char) {
             $binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
   }
   return $binaryip;
}

function bits_to_inet($bits) {
    $inet = "";
    for($pos=0; $pos<128; $pos+=8) {
        $inet .= chr(bindec(substr($bits, $pos, 8)));
    }
    return inet_ntop($inet);
}

$ip = "::ffff:5.39.28.128";
$netmask = 122;

// Convert ip to binary representation
$bin = inet_to_bits($ip);

// Generate network address: Length of netmask bits from $bin, padded to the right
// with 0s for network address and 1s for broadcast
$network = str_pad(substr($bin, 0, $netmask), 128, '1', STR_PAD_RIGHT);

// Convert back to ip
print bits_to_inet($network);

输出:

::ffff:5.39.28.191

【讨论】:

  • +1 感谢您的回答。此代码是否支持真正的 IPv6 地址以及 IPv6 样式的 IPv4?
  • 是的,不需要更改。
【解决方案2】:

解决方法很简单:

// Your input data
$networkstart = '5.39.28.128';
$networkmask = 122;

// First find the length of the block: IPv6 uses 128 bits for the mask
$networksize = pow(2, 128 - $networkmask);

// Reduce network size by one as we really need last IP address in the range,
// not first one of subsequent range
$networklastip = long2ip(ip2long($networkstart) + $networksize - 1);

$networklastip 将具有该范围内的最后一个 IP 地址。

现在,这仅适用于 IPv6 地址空间中的 IPv4 地址。否则,您需要使用 IPv6 到/从 128 位整数函数而不是 ip2long/long2ip。但是,上面的 MaxMind 数据代码使用就足够了,因为我还没有看到任何来自它们的实际 IPv6 数据。

【讨论】:

  • +1 感谢您的回答。对 IPv6 地址做同样的事情是不是一个简单的改变?
  • 是的,基本上你在处理长整数。在 IPv4 中是 32 位长,在 IPv6 中是 128 位长。围绕前缀的数学完全相同:(最大前缀长度 - 给定前缀长度)的 2 次方为您提供块大小。将块大小添加到起始地址,您将获得下一个块的起始地址。减一,你得到块的最后一个地址。
  • 我明白了。所以这是将 ip2long 和 long2ip 换成等效的 IPv6 版本的情况吗?你知道这些功能吗?我在 php.net 上找不到任何东西。
  • 没有等效的 IPv6 版本,因为 IPv6 地址是 16 字节长,而 php 本身最多只支持 8 字节。您必须使用字符串表示形式(就像我在回答中所做的那样)或像 BC MathGMP 这样的大整数扩展名。
  • @Philip 我对您的解决方案的唯一问题是效率低下。加载 MaxMind 数据需要大量的转换,而且速度会非常慢。但是,我支持您对 BC Math 或 GMP 的想法——两者都可能做得很好。另一种选择是将 IPv6 分成四个双四边形并将它们转换为整数。然后按照我提供的相同方式涂抹面膜。没什么复杂的,但需要一些编码。性能方面,我想它应该不错。
猜你喜欢
  • 2014-07-26
  • 2013-08-14
  • 2010-12-01
  • 1970-01-01
  • 2012-10-06
  • 2012-12-28
  • 1970-01-01
  • 1970-01-01
  • 2017-10-08
相关资源
最近更新 更多