【问题标题】:Find the network distance between two IPv4 addresses (not geographic distance)查找两个 IPv4 地址之间的网络距离(不是地理距离)
【发布时间】:2013-07-15 23:07:19
【问题描述】:

给定一个 IPv4 地址(needle)和一个未排序的 IPv4 地址数组(haystack),我如何以编程方式确定给定 haystack 中的哪个单个地址最接近(网络上,而不是地理上)needle?

由于我无法访问每个地址的网络掩码,因此解决方案应忽略网络掩码和 traceroute 类似选项。

使用各种地址,我的意思是:私有、保留、广播、局域网和广域网。

欢迎任何理论、伪代码、python、php 或 perl 形式的帮助。

Getting IP address list between two IP addresses 的问题大致相似,但确实很切题。

【问题讨论】:

  • 您能否解释一下“最接近网络”的含义?
  • 不确定您所说的最接近是什么意思?至于你的各种地址,都只是IP,没有网络掩码,地址的网络/广播状态是未知的。 LAN/WAN 用于人类理解。几乎和IP是一个IP是一个IP。话虽这么说,您的意思是如果给出 10.10.0.1 的针头和 '10.20.0.1, 10.30.0.1, 10.10.0.0, 10.10.0.3' 的草垛,程序会吐回 10.10.0.0 吗?因为它是数字上最接近的?
  • 在 perl(和 python)中也像 Net::Cidr 但没有掩码会变得很困难..
  • @PeterGibson 最接近我的意思是, 192.168.1.101 更接近 192.168.56.1 而不是 172.30.130.66 。并且 192.168.1.254 更接近 192.168.1.240 而不是 192.168.2.1
  • @msantos 但是你应用了什么规则,因为如果忽略子网掩码,192.168.2.1 距离 192.168.1.254 2 IPS,距离 1.240 14 IPS(假设 > /23 掩码)。在您的示例中,您仍在应用子网。如果您只是在寻找“距离”,请将两个 ips 转换为整数并查看它们之间差异的绝对值

标签: php python networking


【解决方案1】:

我仍然不太确定你在问什么,但根据你的评论

@PeterGibson 最接近我的意思是,192.168.1.101 更接近 192.168.56.1 比 172.30.130.66 。并且 192.168.1.254 更接近 192.168.1.240 而不是 192.168.2.1

您可以尝试以下 python 代码来获取距离函数:

import socket
def dist(a, b):
    def to_num(addr):
        # parse the address string into integer quads
        quads = map(ord, socket.inet_aton(addr))
        # spread the quads out 
        return reduce(lambda x,y: x * 0x10000 + y, quads)
    return abs(to_num(a) - to_num(b))

返回的数字相当随意,但应该足以满足基本需求。不过,我仍然不确定它应该如何处理广播地址等。

一些例子:

>>> dist('192.168.1.254', '192.168.1.240')
14L
>>> dist('192.168.1.254', '192.168.2.1')
65283L
>>> dist('192.168.1.101', '192.168.56.1')
3604380L
>>> dist('192.168.1.101', '172.30.130.66')
5630092231245859L

【讨论】:

  • PHP 中类似的方法是
     $min = PHP_INT_MAX; $rtn = 假; $l1 = ip2long($src_ip); foreach($list as $ip) { $l2 = ip2long($ip); $d = ($l1 > $l2) ? $l1 - $l2 : $l2 - $l1;如果 ($min > $d) { $rtn = $ip; $min = $d; } } 回声“$rtn\n”;
  • @msantos,您的解决方案似乎不符合您的要求,即192.168.1.254 更接近192.168.1.240 而不是192.168.2.1 而我的解决方案。
  • @PeterGibson 为什么是 'x * 0x10000' 而不是 'x * 0x100' ?
  • @auxten 强调 OPs 子网概念之间的区别(即 192.168.1.254 更接近 192.168.1.240 而不是 192.168.2.1)
【解决方案2】:

考虑到保留的 IP 地址 (http://en.wikipedia.org/wiki/Reserved_IP_addresses#Reserved_IPv4_addresses),我首先将数组分组。然后我会按地区将它们分开。也许一些树结构就足够了:根节点是顶级区域,然后靠近叶子,它适用于较小的区域。

【讨论】:

  • 问题忽略了IP是广播、组播、私有等。它处理从0.0.0.0到255.255.255.255的所有IP完全相同的方式。目标是找到最短路径。而是找出两个地址之间有多少 IP。
  • 哦,像这样DISTANCE('192.168.0.1', '192.168.0.5') == 4
  • 然后只需将您的地址转换为十六进制,并使用一些二叉搜索树
  • DISTANCE('192.168.0.1', '192.168.0.5') == 4 DISTANCE('192.168.1.1', '192.168.0.252') == 4 ,但是因为 192.168.0.1 和 192.168.0.252很可能在同一个网络中,所以它们是最近的节点。
  • 这是一个相当大的假设,即它们很可能在同一个网络中。也许在基于 soho 的路由器的世界中,它们是。但是除了 /24 之外还有很多使用掩码的网络
【解决方案3】:

不是最漂亮的方法,但由于不需要非常高的性能,它可以完成工作。

<?php

$ip = isset($argv[1]) ? $argv[1] : '192.168.1.254';
printf("Input => %s => %u\n", $ip, ip2long($ip));

var_dump(
    net_distance(
        $ip,
        array(
            '87.86.85.84',
            '173.194.41.170',
            '192.168.1.150',
            '192.168.1.245',
            '192.168.2.2',
            '192.168.56.50',
        )
    )
);

// The problem assumes that:
// a) any local (127) and lan (10,172,192) IPs are closer to each other than to any wan IP.
// b) any wan IP is closer to local and lan IPs than to other wan IP.
function net_distance($src_ip, $list)
{
  if (in_array($src_ip, $list)) {
    return $src_ip; // exact match
  }

  list($a0, $b0, $c0, $d0) = explode('.', $src_ip, 4);

  $triples = array();
  $doubles = array();
  $singles = array();

  foreach($list as $ip) {
    list($a1, $b1, $c1, $d1) = explode('.', $ip, 4);

    if ($a0 == $a1 && $b0 == $b1 && $c0 == $c1) { // match A.B.C.x
      $triples[] = $ip;
    }
    elseif ($a0 == $a1 && $b0 == $b1) { // match A.B.x.y
      $doubles[] = $ip;
    }
    elseif ($a0 == $a1 || (in_array($a0, array(127, 10, 172, 192)) && in_array($a1, array(127, 10, 172, 192)))) {
      $singles[] = $ip; // match A.x.y.z or both As are *likely* to be lan addresses
    }
  }

  if (count($triples) > 0) {
    $list = $triples;
  }
  elseif (count($doubles) > 0) {
    $list = $doubles;
  }
  elseif (count($singles) > 0) {
    $list = $singles;
  }

  $min = PHP_INT_MAX;
  $rtn = false;
  $l1 = ip2long($src_ip);
  foreach($list as $ip)
  {
    $l2 = ip2long($ip);
    $d = ($l1 > $l2) ? $l1 - $l2 : $l2 - $l1;
    // echo "\t" . str_pad($ip, 15, ' ', STR_PAD_RIGHT) . " => $l2 => $d\n";
    if ($min > $d) {
      $rtn = $ip;
      $min = $d;
    }
  }

  return $rtn;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 2014-02-02
    相关资源
    最近更新 更多