【发布时间】:2011-08-26 08:59:16
【问题描述】:
如果您的启用 IPv6 的主机具有多个全局范围地址,您如何以编程方式识别 bind() 的首选地址?
示例地址列表:
eth0 Link encap:Ethernet HWaddr 00:14:5e:bd:6d:da
inet addr:10.6.28.31 Bcast:10.6.28.255 Mask:255.255.255.0
inet6 addr: 2002:dce8:d28e:0:214:5eff:febd:6dda/64 Scope:Global
inet6 addr: fe80::214:5eff:febd:6dda/64 Scope:Link
inet6 addr: 2002:dce8:d28e::31/64 Scope:Global
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
在 Solaris 上,您可以使用接口标志指示首选地址,并且可以通过SIOCGLIFCONF 以编程方式获得:
/usr/include/net/if.h:
#define IFF_PREFERRED 0x0400000000 /* Prefer as source address */
如接口列表中所列:
eri0: flags=2104841<UP,RUNNING,MULTICAST,DHCP,ROUTER,IPv6> mtu 1500 index 2
inet6 fe80::203:baff:fe4e:6cc8/10
eri0:1: flags=402100841<UP,RUNNING,MULTICAST,ROUTER,IPv6,PREFERRED> mtu 1500 index 2
inet6 2002:dce8:d28e::36/64
虽然这不能移植到 OSX、Linux、FreeBSD 或 Windows。尽管从管理员的角度来看,基于 UUID 的适配器名称(取决于 Windows 版本)完全无用,但 Windows 很容易被放过。
对于 Linux this article 详细说明了如何更改参数 preferred_lft(其中 lft 是“生命周期”的缩写)以对内核的选择过程进行加权。不过,此设置在SIOCGIFCONF 或getifaddrs() 的结果中并不方便使用。
所以我想绑定到eth0、eri0 或任何可用的接口名称。选择有点明显:
- 适配器名称解析为多个接口时失败。我采用这种方法来处理多播传输 (OpenPGM),因为协议必须只有一个发送地址。
- 绑定到所有东西。这是一种逃避,对用户来说是出乎意料的。
- 使用
SO_BINDTODEVICE绑定到适配器。这需要 Linux 上的CAP_NET_RAW系统功能,这对于管理员来说可能是相当麻烦的开销。 - 绑定到适配器上的第一个 IPv6 接口。排序往往是完全虚假的。
- 绑定到最后一个接口。 David Croft 的文章暗示 Linux 会这样做,但也有点假。
- 枚举每个接口并为每个接口显式创建一个新套接字。
使用选项 #6,我希望您通常会更聪明,并采取这样的方法:如果只有一个链接本地范围地址可用,则绑定到该地址,否则只绑定到可用的全局链接范围地址。
当连接到另一台主机时,可以使用RFC 3484,但正如您所见,所有选择都取决于匹配目标地址:
- 首选相同的地址。 (即目标是本地机器)
- 首选合适的范围。 (即与目标共享的最小范围)
- 避免使用已弃用的地址。
- 首选家庭地址。喜欢传出 界面。 (即更喜欢我们发送的接口上的地址 出)
- 首选匹配标签。
- 首选公共地址。
- 使用最长匹配前缀。
在某些情况下,我们可以在这里使用 #7,但在上面的接口示例中,两个全局范围接口都有 64 位前缀长度。
RFC 3484 有以下相关行:
IPv6 寻址架构5 允许多个单播
分配给接口的地址。这些地址可能有
不同的可达性范围(链接本地、站点本地或全局)。
这些地址也可能是“首选”或“弃用”6。
指向RFC 2462的链接,同样展开:
首选地址 - 分配给其使用的接口的地址 经过 上层协议不受限制。首选地址可能 用作发送数据包的源(或目标)地址 从(或到)界面。
但没有任何方法可以以编程方式获取此详细信息。
对 Win32 API 的支持,它公开了一个 ioctl SIO_ADDRESS_LIST_SORT,它允许开发人员不仅可以使用 RFC 3484 排序,还可以考虑任何系统管理员覆盖。 Linux 有/etc/gai.conf 用于getaddrinfo() 中的RFC 3484 排序,但没有用于直接访问排序的API。 Solaris 有ipaddrsel 命令。 OSX 通过在 10.7 中添加 ip6addrctl 来跟随 FreeBSD。
编辑:在这个额外的 IETF 草案文档中列出并提到了一些与 RFC 3484 排序有关的问题:
https://datatracker.ietf.org/doc/html/draft-axu-addr-sel-01
例如,Solaris 为每个新的
创建新的别名接口 分配给物理接口的地址。所以 if_index 也可以是
用于唯一标识一个源地址特定的路由表
那个平台。其他操作系统的工作方式不同。
作者喜欢 Solaris 给每个额外的 IPv6 接口一个新别名的方法,这样eri0 将成为链接本地范围地址,并且必须指定 eri0:1 或 eri0:2 等以使用全局-范围地址。
很明显,虽然这是一个不错的想法,但在相当长的一段时间内人们都不能指望看到其他操作系统发生变化。
【问题讨论】:
-
您想绑定到特定地址而不是让操作系统为您选择的原因是什么?
-
@Sander 部署在多 NIC 环境中,例如您可能拥有 Internet、DMZ、Intranet,或者拥有专用于某些流量的网络,例如原始传入数据、处理后的数据和客户端流量.
-
我明白了。通常我会尝试确保默认行为有效,例如通过使用全局地址进行外部通信和使用 ULA 用于内部 NIC,以便最长前缀匹配有效,但这并不总是可行的。并且在发送到多播目的地时它不起作用,因为 ULA 总是有更长的匹配。在配置文件中指定要绑定的源地址是一个选项..
-
我知道它有点过时了。但是您找到问题的答案了吗?
-
@Sergey 还没有,我会定期审查每个新平台,看看是否出现了实现上述功能的新选项。