【问题标题】:Is IP address on the same subnet as the local machine (with IPv6 support)IP 地址是否与本地计算机位于同一子网上(支持 IPv6)
【发布时间】:2011-04-10 10:17:13
【问题描述】:

是否有人有一些代码可以确定 IP 地址(IPv4 或 IPv6)是否与运行应用程序的机器位于同一子网上?我已经看到了许多使用 IPv4 执行此操作的代码示例,但我找不到任何支持 IPv6 的代码示例。

编辑:

我不确定我是否了解 v4 和 v6 之间的所有差异,所以我的问题还有一点。我有一个同时服务于 Internet 客户端和 Intranet 客户端的应用程序,也就是说,有一些客户端与服务器位于同一物理网络上。所以有时客户端之间有路由器,有时没有。使用 IPv4,我可以通过检查客户端 IP 地址与服务器 IP 地址和子网来确定这一点,所以如果我的服务器的 IP 和子网掩码分别是:

192.168.123.15 255.255.255.0

服务器收到来自 192.168.123.100 的客户端请求,我知道客户端和服务器之间没有路由器。但是,如果服务器收到来自 192.168.1.100 或 67.7.23.4 的客户端请求,我知道在这些客户端和服务器之间有一个路由器。在 .Net 中,我可以收集客户端和服务器 IP 地址(v4 和 v6),但找不到 IPv6 子网掩码。

有没有办法在 .Net 中收集这些信息,还是我误解了 IPv4 和 IPv6 之间的一些区别?

编辑 x2:

我在 MS Connect 站点上发布了这个,看看他们是否正在处理这件事,或者他们没有向 UnicastIPAddressInformation 类添加 IPv6Mask 属性是否有原因。

https://connect.microsoft.com/VisualStudio/feedback/details/643031/unicastipaddressinformation-class-has-no-ipv6mask-property

大约在同一时间,我还在 MSDN 论坛上发布了相同的问题。 1800+ 次浏览,没有一个回复。我想我不是唯一对此感到好奇的人。

http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/dd30e161-9be5-4d70-97c0-22e2756ce953

【问题讨论】:

  • 如何提供 IPv6 地址?是否使用了转换机制或者是 DHCPv6?
  • 好吧,我正试图让它在任何情况下都能发挥作用,所以让我们说两个?
  • 也许这真的应该从 serverfault.com 询问
  • 不,这是一个关于如何以编程方式确定客户端和服务器是否在同一个子网上的问题。

标签: c# .net ipv6 subnet


【解决方案1】:

看起来框架没有办法做到这一点。最准确的方法是进行路线查找,但我看不到在 C# 中这样做的好方法。 (在 Linux 下,我会执行 /sbin/ip -6 route get <ipv6-addr> 并查看返回哪个路由。)您必须在 Windows 中找到一个本地调用来执行此操作;我没有看到命令行应用程序。

最好的方法可能是解析netsh interface ipv6 show route verbose 的输出。您可以查找任何非/128 前缀并对其进行最长前缀匹配。 (好吧,如果你点击 /128,那就是分配给盒子的地址)

您还可以查看邻居表。 (netsh interface ipv6 show neighbors),但如果您最近没有与该主持人交谈,那可能不包含您要查找的条目。

您需要考虑的其他潜在问题:

  • Link-local addresses (fe80::/10)(以及多播、环回和未指定 - 表中的所有内容)
  • 事实上,在 IPv6 中,分配的地址并不意味着链接上的前缀。前缀表是单独的。目前尚不清楚如何在 Windows 下对此进行测试,但 netsh interface ipv6 show siteprefixes 可能会有所帮助。看起来 Windows 实际上可能比标准预期的更像 IPv4。

编辑:听起来检查邻居表将是您执行此操作的阻力最小的路径;如果您正在接受来自 Intranet 的连接,然后转身检查邻居表,您可以合理地确定如果邻居是本地的,它将存在于表中。如果您检查邻居表,小心只查看 LAN 接口的邻居表。 (许多 Windows 系统默认安装的 ISATAP 接口将整个 IPv4 互联网公开为链接本地“子网”。)

同样,IPv6 地址没有“网络掩码”的概念,因为在线前缀表与地址分配是分开的。 但是,如果您的服务器位于某处,您可能 99% 确定它位于 /64 上。 (尽管您必须小心;如果它本身是隧道端点,有时我会看到为 6in4 隧道分配更长的前缀)所以一个快速而肮脏的算法是:

  • 忽略前 64 位为 0 的所有地址(本地环回)
  • 忽略与 ff00::/8 匹配的所有地址(多播)
  • 如果地址与 fe80::/10 匹配,则为 interface local。请注意这一点,因为如果您启用了 ISATAP 接口,“link-local”意味着“到整个 IPv4 互联网的自动隧道”! (不好。)所以最好不要信任链接本地地址,除非您确定它们来自 LAN 接口。 (无法路由)
  • (现在是复杂的部分)确定地址是否为邻居。 (邻居查找)hack-and-slash 解决方案是检查系统上配置的所有 IPv6 地址(通过无状态地址自动配置、DHCPv6 或静态自动配置,如果可以确定的话)并检查前 64 位。 在服务器上(假设没有用非/64 前缀配置时髦的隧道)在极少数情况下这将是一个错误,因为 - 再一次 - 你不能确定地址是否真的打开 -链接,除非您检查 on-link 前缀表。 (Windows 没有这个概念;它似乎存储在路由表中。)大多数可以配置为在以太网接口上发送路由器通告的网络设备将始终通告 /64 前缀。如果您可以检查数据包是否来自 LAN 接口,那么这将不太可能是错误。

编辑 2

我已经编写了一些代码来解析 IPv6 路由表并将其发布到here。它还没有解决这个问题中提出的难题,但这是朝着正确方向迈出的一步。

【讨论】:

  • 必须有一种比生成应用程序并查看其输出更本地化的方法。
  • @Steven,如果您想使用 P/Invoke 并找出调用,那将起作用 - 或者找一个编写了相同功能的 Windows .NET 库的人。我认为生成具有众所周知的输出的应用程序可能比进行本地调用更不容易出错(希望netsh 会在本地调用发生变化时在不同的 Windows 版本中更新 - 生成应用程序可以使您免受潜在的混乱)
  • 您可能是对的,实际上,您说命令行界面实现起来更快并且更有可能在变化中存活下来。不过,不知何故,我不能赞同它。
  • 好吧,我认为这不是一个很好的答案,但看起来还没有答案,所以我要奖励你,因为我感谢你投入的大量工作这个。
  • @Spencer,谢谢,我正在考虑为这个问题编写一些 C# 代码,但没有解决 - 日常工作要做的工作太多;-)。如果我这样做了,我会更新我的答案。
【解决方案2】:

IPv6 中 IPv4 网络掩码的道德等价物称为 前缀长度。 (其实我们也喜欢在 IPv4 中讨论前缀长度而不是网络掩码,但是有些人还没有得到备忘录。)

IPv6 中没有出现 IPv4 的另一个问题是,默认路由器通过回答来自主机的路由器请求查询来宣传它们在链路上的存在。主机会保留一份他们以这种方式找到的所有默认路由器的列表,以及它们的有效和首选生命周期。路由器还可以通告零个、一个或多个前缀,它们用作默认路由器,并且主机会保留这些前缀的列表以及它们的关联路由器以及它们各自的有效和首选生命周期。

每个前缀在广告中都有两个辅助位 A 和 L,当它们被添加到前缀列表时,它们会被主机合并。 A=1 位表示是否允许主机自动配置带有该前缀的接口地址,而 A=0 表示主机需要通过 DHCPv6 或手动获取带有该前缀的地址。 L=1 位表示前缀是“on link”,主机可以使用邻居发现(ARP 的 IPv6 等价物)直接通过网络发送,而 L=0 表示前缀是“off link”,并且主机需要将该前缀的所有流量发送到默认路由器。

长话短说:如果您想知道某个 IPv6 地址“在链接中”,那么您必须遍历 IPv6 前缀列表并将每个前缀与地址进行比较,并查看 L 位以确保它是链接前缀。唉,我只知道 BSD 系统查看前缀列表的方式,即sysctl(ICMPV6CTL_ND6_PRLIST, ...)。真的不确定 MSFT 是否为此向 C# 开发人员提供了任何东西。

是的,我知道这不是一个完整的答案。唉。

【讨论】:

  • 是的,信息越多越好。 +1 因此,我相信 IPv4 网络上希望与另一台计算机建立连接的客户端确定是否需要将数据包发送到默认网关的方式是比较目标 IP 地址、客户端自己的网络掩码和客户拥有自己的IP地址。如果应用于两个 IP 地址的掩码不匹配,则数据包将被发送到网关。您能否详细解释一下客户端如何在 IPv6 中执行此操作以及它使用哪些信息?
  • IPv4 没有“off-link”与“on-link”的概念,因此,如果目标地址与接口地址共享相同的前缀,则假设它在相同的链路,并且要求主机对目的链路层地址进行 ARP。这就是为什么你在 IPv4 中所做的就足够了。
  • 在 IPv6 中,主机在决定是否为链路层地址发送邻居请求时不会查看接口地址。相反,主机查询前缀列表,该列表是根据接收路由器通告的结果构造的,以响应路由器请求(也可以通过手动静态配置)。
【解决方案3】:

我一直在寻找 wmi 文档,试图找到 ipv6 前缀,但我也在努力寻找它。但是,我有 IPv4 的工作代码。

我认为您可以放心地假设该网段的前缀是 /64。如果你这样做,你可以只比较每个地址的前 8 个字节。运行应用程序的单个本地网络通常是 /64,即使点对点链接也经常被赋予完整的 /64 范围,即使只使用了 2 个。

【讨论】:

  • +1, RFC 4291 需要 64 位接口标识符,尽管 RFC 4862 保留了非 /64 前缀长度的可能性。这是最简单的解决方案(带有我在回答中提到的警告)
  • 是的,由于 64 位接口标识符的要求,通过路由器广告的地址分配也仅适用于 /64 前缀。您可以静态使用不同的或与 DHCPv6 一起使用,但我从未见过除了 /64 之外的其他任何东西。
【解决方案4】:

在 99% 的情况下,这很简单。 IPv6 中的所有子网都是 /64 前缀,因此如果前缀的最左边 64 位相同,则它们位于同一子网中。

现在确实有些人正在做一些奇怪的事情并制作具有更长前缀的子网,但其中大部分是处​​理点对点电路,他们使用 /126 对两个端点进行编号,然后保留 / 64 包含 /126。应用程序永远不会遇到这种情况,因为不涉及任何服务器。

没有充分的理由支持 /64 以外的子网大小。

【讨论】:

  • 我不知道我是否同意“没有充分的理由支持 /64 以外的子网大小。”违反规范是我们最终使用 Internet Explorer 的方式。
猜你喜欢
  • 2016-10-03
  • 2021-12-12
  • 2012-09-11
  • 2016-08-29
  • 2021-05-22
  • 1970-01-01
  • 2014-04-15
  • 2023-04-02
  • 1970-01-01
相关资源
最近更新 更多