IP地址是否与本地计算机位于同一子网上(支持IPv6)

有没有人有一些代码可以确定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站点上发布了这个,看看它们是否正在处理,或者是否有原因他们没有将IPv6Mask属性添加到UnicastIPAddressInformation类。

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

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

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

您还可以检查邻居表。 ( netsh interface ipv6 show neighbors ),但如果您最近没有与该主机通信,则可能不包含您正在寻找的条目。

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

  • 链路本地地址(fe80 :: / 10) (以及多播,环回和未指定 – 表中的所有内容)
  • 事实上,在IPv6中,分配的地址并不意味着链接前缀。 前缀表是分开的。 目前尚不清楚如何在Windows下测试,但netsh interface ipv6 show siteprefixes可能有所帮助。 看起来Windows实际上可能更像IPv4而不是标准期望。

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

同样,IPv6地址没有“网络掩码”的概念,因为链接前缀表与地址分配是分开的。 但是 ,如果你有一台服务器坐在某个地方,你可能已经99%确定它在/ 64上 。 (虽然你必须要小心;如果它本身就是一个隧道端点,有时我看到为6in4隧道分配了更长的前缀)所以一个快速而肮脏的算法将是:

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

编辑2

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

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

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

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

简而言之:如果您想知道IPv6地址是“在链接上”那么您必须遍历IPv6前缀列​​表并将每个地址与地址进行比较,并查看L位以确保它是一个on-link字首。 唉,我只知道查看前缀列表的BSD系统方式,即sysctl(ICMPV6CTL_ND6_PRLIST, ...) 。 真的不确定MSFT为C#开发人员提供了什么。

是的,我意识到这不是一个完整的答案。 唉。

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

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

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

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

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