在PHP中使用IPv6

我们最近收到了LIR状态和/ 29 IPv6阻止。 然后,需要跟踪指定的子网。 而且由于我们的帐单是用PHP编写的,所以我不得不从这个问题中得到一些启发,并意识到这种语言并不是使用IPv6时最友好的语言。 削减-我们解决地址和范围问题的解决方案。 也许不是最优雅,但它执行任务。



一点理论


免责声明 如果您熟悉什么是IPv6以及将其与之搭配使用,那么这部分可能对您来说很无聊。 可能不是。

第一次看到IPv6注释的人可能会灰心。 在优雅的64.233.177.101之后,我们突然遇到2607:f8b0:4002:c08 :: 8b并可能感到困惑。 两者以及另一个-分别是人类可读的32位和128位表示。 任何IP数据包都包含一个标头,每个标头都经过严格标准化的分配。 在不深入了解标头的结构的情况下,我们需要从这里得到一件事:对于具有IP地址和范围的操作,使用二进制数学和按位运算通常很方便。 将它们存储为数据库中的BINARY(4)和IPv6的BINARY(16)也是最方便的。

应解决的另一个重要方面是网络掩码和CIDR表示法。 CIDR是无类域间路由的缩写。 在确定IP地址的哪一部分是网络前缀以及哪一部分是该网络内的网络接口地址的问题上,此概念替代了第一类。 实际上,与前缀相对应的前n位将设置为1,其余为0。

以人类可读的形式,它写为ip.add.re.ss./cidr 。 例如, 64.233.177.0/ 24表示前24位是指前缀。 最后8位是人类可读条目中的最后一个数字,指的是子网内的地址。 多做几次练习。 64.233.177.101/322607:f8b0:4002:c08 :: 8b / 128-一个特定的地址。 2607:f8b0:4002:c08:// 64-前64位(前4组)-前缀,其余64位-本地部分。 顺便说一句,如果有人为条目中的“ ::”感到尴尬,则双冒号替换任意数量的包含0的节。它只能在注释中出现1次。 换句话说, 2607:f8b0:4002:c08 :: 8b = 2607:f8b0:4002:c08:0 0:0 0:8b

我们需要从这一切中学到什么? 首先,可以使用二进制“与”和“或”获得二进制形式的掩码,从而获得第一个和最后一个子网地址。 其次,可以通过在二进制表示形式的第n个位置加1来计算下一个大小为子网(即CIDR)的子网。 通过二进制视图,我的意思是使用bindpack()inet_pton()函数以及进一步使用按位运算符的结果,即binary-二进制系统中的一种表示形式,可以使用base_convert()获得

历史背景
无类别隔离是在无类别之前进行的。 在那些遥远的年代,没有人期望会有如此多的子网;它们以大块的形式左右分布:A类-前8位(即第一个数字)被前缀,前导0。 B类-前16个(前两个数字),前10位; C类-前24位,即110的前导位。这些相同的前导位设置了发出类地址的范围:A类为0.0.0.0-127.255.255.255 ,A类为128.0.0.0-191.255.255.255 -B类为192.0 .0.0-223.255.255.255-C级。随着互联网在全球范围内的传播,监管机构意识到他们已经错过了,并且在90年代初开发了一种无阶级概念,这使他们不再迷恋领先地位。 比方说,更多的细节可以在无所不知的世界中找到


让我们继续练习


实际上,在我看来,我们执行了三个最可能的任务:

  1. 获取范围的第一个和最后一个地址;
  2. 获得给定大小的下一个范围(CIDR);
  3. 检查地址是否属于范围。

该实现将针对IPv6,但如有必要,可以轻松调整逻辑。 我从这里得到了一些想法,但是实现了一些不同。 同样在示例中,也没有检查输入错误。 所以走吧

正如我已经提到的,可以使用按位运算来确定范围的第一个和最后一个地址,知道范围的开始和二进制子网掩码。 因此,我们需要做的第一件事就是将CIDR转换为二进制掩码。 为此,请收集其十六进制表示并将其打包为二进制。

function cidrToMask ($cidr) { $mask = str_repeat('f', ceil($cidr / 4)); $mask .= dechex(4 * ($cidr % 4)); $mask = str_pad($mask, 32, '0'); return pack('H*', $mask); } 

调用包(“ H *”,$ mask)以与inet_pton()相同的方式打包十六进制表示。 唯一的区别是,当您调用pack()时,与人类可读的条目相反,条目必须全为0,并且条目中不应包含冒号。

下一步是计算范围的开始和结束。 这里有细微差别。 按位操作受处理器容量的限制。 因此,在我有时用于任何测试纵容的32位CubieTruck上,无法一次执行处理地址的所有128位。 但是,没有什么可以阻止我们将其分成32位的组(以防万一,谁知道我们将在哪个处理器上运行)。

 function getRangeBoundary ($ip, $cidr, $which, $ipIsBin = false, $returnBin = false) { $mask = cidrToMask($cidr); if (!$ipIsBin) { $ip = inet_pton($ip); } $ipParts = str_split($ip, 4); $maskParts = str_split($mask, 4); $rangeParts = []; for ($i = 0; $i < count($ipParts); $i++) { if ($which == 'start') { /*  &       . */ $rangeParts[$i] = $ipParts[$i] & $maskParts[$i]; } else { /*  |    (~)           1. */ $rangeParts[$i] = $ipParts[$i] | ~$maskParts[$i]; } } $rangeBoundary = implode($rangeParts); if ($returnBin) { return $rangeBoundary; } else { return inet_ntop($rangeBoundary); } } 

为了将来使用,我们将提供以二进制和人类可读形式传输IP并获得结果的功能。 这里的$ which参数设置我们是否要获取范围的开始或结束(值分别是'start''end' )。

下一个任务(除了对我们公司最实际的任务)是计算下一个范围。 对于此任务,除了如何将地址分解为二进制字符串并在所需位置加1,然后将所有内容折回外,什么都没有想到。 为了防止工件出现在任何地方,我决定在分解和汇编过程中按字节分割地址。

 function getNextBlock ($ipStart, $cidr, $ipIsBin = false, $returnBin = false) { if (!$ipIsBin) { $ipStart = inet_pton($ipStart); } $ipParts = str_split($ipStart, 1); $ipBin = ''; foreach ($ipParts as $ipPart) { $ipBin .= str_pad(base_convert(unpack('H*', $ipPart)[1], 16, 2), 8, '0', STR_PAD_LEFT); } /*  1       "" :) */ $i = $cidr - 1; while ($i >= 0) { if ($ipBin[$i] == '0') { $ipBin[$i] = '1'; break; } else { $ipBin[$i] = '0'; } $i--; } $ipBinParts = str_split($ipBin, 8); foreach ($ipBinParts as $key => $ipBinPart) { $ipParts[$key] = pack('H*', str_pad(base_convert($ipBinPart, 2, 16), 2, '0', STR_PAD_LEFT)); } $nextIp = implode($ipParts); if ($returnBin) { return $nextIp; } else { return inet_ntop($nextIp); } } 

输出将是$ cidr中指定的下一个大小范围的前缀。 使用此功能,我们可以为客户分配地址块。

最后,检查地址是否属于该范围。 例如,我们为分配给客户的/ 64个块分配了一个/ 48个块,并且我们需要确保在约会期间我们不会超出分配的块(实际上这会很快发生,但是仍然有机会)。 这里的一切都很简单。 我们以二进制形式获取范围的开始和结束,并检查地址是否在其中。

 function ipInRange ($ip, $rangeStart, $cidr) { $start = getRangeBoundary($rangeStart, $cidr, 'start',false, true); $end = getRangeBoundary($rangeStart, $cidr, 'end',false, true); $ipBin = inet_pton($ip); return ($ipBin >= $start && $ipBin <= $end); } 

希望对您有所帮助。 您还会发现其他哪些寻址功能有用? 在评论中热烈欢迎任何添加,评论和代码评论。

如果您已经是我们的客户,或打算成为一个客户,在发布本文时,我们建议您向荷兰销售部门索取免费的Block / 64,以获取所有vps服务或荷兰Equinix Tier IV数据中心内的专用服务器的专用服务器。票中的这篇文章。 优惠有效期至2020年3月。

一点广告:)


感谢您与我们在一起。 你喜欢我们的文章吗? 想看更多有趣的资料吗? 通过下订单或向您的朋友推荐给开发人员的基于云的VPS, 最低 价格为4.99美元这是我们为您发明的入门级服务器独特类似物: 关于VPS(KVM)E5-2697 v3(6核)的全部真相10GB DDR4 480GB SSD 1Gbps从$ 19还是如何划分服务器? (RAID1和RAID10提供选件,最多24个内核和最大40GB DDR4)。

阿姆斯特丹的Equinix Tier IV数据中心的戴尔R730xd便宜2倍吗?在荷兰,我们有2台Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100电视 戴尔R420-2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB-$ 99起! 阅读有关如何构建基础架构大厦的信息。 使用价格为9000欧元的Dell R730xd E5-2650 v4服务器的上等课程?

Source: https://habr.com/ru/post/zh-CN484586/


All Articles