我正在使用 .NET Framework 类来获取我的机器的 IP 地址:
Dns.GetHostAddresses(Dns.GetHostName())
我有一个 VirtualBox 适配器,它同时具有 IPv4 和 IPv6 地址。使用 .NET 代码,我获取的 IPv6 地址为fe80::71a3:2b00:ddd3:753f%16
。
注意到%16
最后的了吗?
但是,如果我使用 查询相同的内容WMI
,我会得到地址fe80::71a3:2b00:ddd3:753f
,而不带%16
。
有%16
什么特殊意义吗?
编辑:
我刚刚对此做了一些观察,它们与史蒂芬詹宁斯在他的回答中所说的非常吻合。
我安装了 VMware 来查看它发布的 IPv6 地址。地址是:
fe80::3dd0:7f8e:57b7:34d5%19
fe80::b059:65f4:e877:c40%20
显然,% 后面的数字不是某种十六进制表示。我使用 WMI 检查了网络适配器的所有可用属性,发现这些数字与每个网络适配器的属性完全相同InterfaceIndex
。根据微软,它唯一地标识每个网络适配器,并且此属性是在Vista中引入的。
让我感到困惑的是,IPAddress
除非 IP 地址有效,否则为什么该类会允许您以该格式创建 IP 地址。Stephen 给出了答案:该数字是作用域 ID。IPAddress
有一个接受地址和作用域 ID 的构造函数。
哦,这三个网络适配器都是本地链接。已通过 确认ipconfig
。
太棒了。太有趣了!!
答案1
‘%’后面的数字是范围 ID。
IPv6 为地址定义了至少三个可达范围:
全局可寻址。这是您的 ISP 提供给您的 IPv6 地址。它可在公共互联网上使用。
本地链路。这类似于 169.254.XX 范围。这是计算机为方便本地通信而自行分配的地址。这些地址不会在公共互联网上路由,因为它们不是全球唯一的。
节点本地。这是标识本地接口的地址,类似于 127.0.0.1。基本上,这是地址 ::1。
微软发布了这篇描述 IPv6 寻址的文章,这是我找到的最不令人困惑的文章。文章指出,地址中存在范围 ID 意味着它是链路本地地址。您还可以判断它是链路本地的,因为地址以 开头fe80
。
关于这个话题的清晰、简单易懂的信息似乎很少见,所以我根据我对RFC 4007以及其他信息。
一台计算机可以有多个链路本地地址,每个地址都有不同的范围。范围 ID 指示地址适用于哪个范围。例如,想象一下一台计算机有两个 NIC,每个 NIC 在不同网络上都有一个链路本地地址的情况。如果您尝试将某些内容发送到以 fe80 开头的另一个地址,计算机如何知道要通过哪个 NIC 发送?范围 ID 似乎是解决此问题的方法。
答案2
前缀为 fe80::/64 的 IPv6 地址是链路本地地址,由该前缀与网络设备的硬件地址(示例中为 71a3:2b00:ddd3:753f)组合而成。(IPv4 中的类似地址为 169.254.0.0/16。)由于机器上所有链路本地地址的前缀相同,因此路由有时可能需要知道您指的是哪个接口。这就是百分比后的数字(称为区域索引)指定的。具体情况取决于操作系统:在 Windows 上,%16
是接口编号 16;例如在 Linux 上,您可能会看到类似这样的内容%eth0
。
某些工具或 API 会认为此区域索引对于其目的而言不重要或隐含。例如,在 Linux 上,该ifconfig
工具不会显示它,因为地址属于哪个接口是显而易见的。但一般来说,应该将其考虑在内。
答案3
% 后面的字符(在您的示例中恰好是数字)是“区域 ID”。(“区域 ID”是用于标识要使用哪个网卡的文本。它可能看起来像网卡的名称,或者只是一个数字。)
这些字符用于识别“网络接口”,人们通常称之为“网卡”。例如,它可以帮助确定数据包是使用有线以太网卡还是无线 Wi-Fi 适配器。
我猜你使用的是 Microsoft Windows。它使用数字作为区域 ID。虽然 Microsoft 的文档指出了不同类型的区域 ID(我将在本回答的后面讨论),但对于链路本地地址(在 fe80::/64 中),“区域 ID”是网卡的“接口索引”。
作为比较,类 Unix 系统可能会在 % 符号后使用字母。例如:fe80::71a3:2b00:ddd3:753f%eth0
在这种情况下,区域 IDeth0
与操作系统通常用于识别网卡的名称相匹配。
在 Microsoft Windows 中,您可以使用检查路由表的命令行之一获取(数字)区域 ID 列表。我更喜欢“ netstat -nr
”,因为它在其他操作系统上也有效,但 Microsoft Windows 也支持“ route print
”。报告的结果输出可能会超过一屏长,因此请准备好向后滚动,除非您通过管道传输更多内容。
例如,在我的系统上:
=========================================================================== Interface List 14...5c f9 dd 6d 98 b8 ......Realtek PCIe GBE Family Controller 12...e0 06 e6 7e fc 4e ......Bluetooth Device (Personal Area Network) 1...........................Software Loopback Interface 1 13...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter 15...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter #2 ===========================================================================
在这种情况下,类似这样的地址fe80::71a3:2b00:ddd3:753f%14
将引用与“区域 ID”相关的网卡14
,而该网卡恰好是我的 Realtek PCIe GBE 系列控制器。(“GBE”指的是千兆以太网。)
(您还可以看到许多 Microsoft Windows 版本使用的这些“接口索引”值,例如:“ WMIC NICCONFIG GET Caption,InterfaceIndex,IPAddress /FORMAT:LIST
”。在 PowerShell 中尝试这样做的人应该记住在每个逗号前使用反引号。)
现在,这里有一个棘手的部分:如果你想 ping 远程地址,你可能需要使用远程系统的 IPv6 地址,但本地系统的“区域 ID”。因此,例如,如果我正在使用计算机 A,并且我有一个连接到接口号 14 的本地 IPv6 地址 fe80::1,并且我想 ping 计算机 B,并且它有一个连接到接口号 16 的本地 IPv6 地址 fe80::2,那么我将使用以下内容:
ping fe80::2%14
因此,该ping
命令将把 ICMPv6 数据包发送到属于远程计算机的远程 IPv6 地址 (fe80::2),并使用接口区域 ID 14 来执行此操作。区域 ID 14 是我正在使用的系统的数字,而不是远程系统的数字。因此,远程系统的地址和指定的区域 ID 来自不同的计算机。
现在,让我们看看为什么这是必要的。
如果我想 ping Google 的 IPv6 地址(在我写这个答案时是 2607:f8b0:400a:802::200e),那么路由表将检查哪个网卡处理以 2607:f8b0:400a:802 开头的地址。路由表将指示我的网卡都没有直接连接到使用以 2607:f8b0:400a:802 开头的地址的网络,所以我的计算机最终将使用“网关”地址。如果我连接到我所在组织的另一个网络,我可能有一个特殊的“网关”地址,用于将流量路由到专用网络。在这种情况下,我没有更具体的网关,因此我将使用 IPv6“默认网关”。除了链路本地地址外,IPv6 在大多数情况下都是这样工作的。这也是 IPv4 在大多数情况下的工作方式。 (我确实通过假设 IPv6 子网大小为 /64 来简化这个例子,因为描述整个过程会使这个描述更长。)
根据RFC 4291 第 2.8 节,每台使用 IPv6 的计算机都应为每个网络接口分配一个链路本地地址。 RFC 4291 第 2.5.6 节显示了链路本地地址必须以哪些位开头,这导致链路本地地址以“fe80:0000:0000:0000:”开头(尽管其中许多零被折叠为双冒号)。这些地址以“fe80:”开头的事实也由RFC 4291 第 2.4 节。
如果您尝试 ping 远程系统(例如“2607:f8b0:400a:802”),一般过程通常是找出该地址所属的网络或子网,这可以通过查看地址开头的位来完成。然后,这些位用于确定如何路由流量。
但是,该过程不适用于 IPv6 链路本地地址,因为每个单个(可操作、活动)网络接口都有一个以“fe80:”开头的链路本地地址,位于使用子网前缀/大小为“/64”的子网上。如果您使用的是笔记本电脑,您可能会发现您的以太网卡和 Wi-Fi 适配器都需要有这样的 IPv6 地址。
现在,当您向 fe80::2 发送 ping 命令时,您希望计算机将该数据包发送到正确的网卡。如果您的打印机连接到有线网络,您不希望使用不会导致流量到达打印机的网络路径/路由将流量发送到 Wi-Fi 卡。如果您尝试使用 Wi-Fi 卡与无线设备通信,您不希望流量从以太网卡发出。
解决方案是让您指定要让流量使用哪个网络设备。这就是区域 ID 的目的。
答案更新:
-
这个答案的原始版本(在我提供的答案中,这个答案评价相当高)最初使用了术语“接口标识符”而不是“区域 ID”。
-
我认为“接口标识符”这个术语只是我个人编造的一个术语,它基于“接口索引”这个术语,Microsoft Windows 使用它来标识特定的网卡。然而,“接口 ID”这个术语被证明是一个不太理想的选择,因为 IETF RFC 文档已经将该术语用于其他用途:
- RFC 1884:“IP 版本 6 寻址架构”使用该短语表示 IPv6 地址末尾的某些位。(在一些不同的示例中,位数似乎有所不同,但该标识符通常基于网卡的 MAC-48 地址。)
- RFC 5453:保留的 IPv6 接口标识符说(早期),“IPv6 单播地址由两部分组成:子网前缀和标识子网前缀内唯一接口的接口标识符(IID)。”
- “区域 ID” 一词源自RFC 4007:“IPv6 范围地址架构”,第 11 节“文本表示”(尤其是第 11.2 节)使用了术语“zone_id”。虽然我认为这样的 IETF RFC 是最权威的,但我也遇到了Microsoft Windows Server 2003 文档:了解 IPv6 的更新,PDF 第 11 页(打印第 7 页)将其称为“区域标识符 (ID),也称为范围 ID”。它给出了一个例子,其中 fe80::/64 范围内的“区域 ID”是“包含目标地址的链接所连接的接口的接口索引”,但与 fec0::/48 范围中的例子不同,其中“区域 ID”是“包含目标地址的组织站点的站点 ID”。
当我偶然发现 IETF RFC 使用的另一个术语时,我决定立即更新这个流行的答案,以使其更符合这样一套更官方的标准。 (尽管最初的版本在编写时很有帮助,但我希望我的文档不要与官方现有的 IETF RFC 标准在 IPv6 地址上对同一短语的记录有所不同。所以这是 2020 年 1 月 19 日更新的主要原因。)
-
我认为“接口标识符”这个术语只是我个人编造的一个术语,它基于“接口索引”这个术语,Microsoft Windows 使用它来标识特定的网卡。然而,“接口 ID”这个术语被证明是一个不太理想的选择,因为 IETF RFC 文档已经将该术语用于其他用途:
-
除了将“接口标识符”更新为“区域 ID”之外,2020 年 1 月 19 日更新中还发生了其他细微变化:
- 添加了 WMIC 命令以获取索引标识符(在 Microsoft Windows 中)
- 已修复地址中的拼写错误(将 fe80 写成了 fd80)
- 细微调整,旨在使一些文字更加清晰
- 添加了相关技术文档的超链接。(实际上,此类超链接主要包含在本节中,该节描述了此答案的各个部分如何更新。)