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上使用。但是,从管理员的角度来看,Windows非常容易解决,因为它具有完全无用的基于UUID的适配器名称(取决于Windows版本)。
对于Linux,本文详细介绍了如何调整参数preferred_lft
(其中lft
缩写为“lifetime”),以通过内核加权选择过程。然而,在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
用于RFC 3484排序在getaddrinfo()
中,但没有API直接访问排序。Solaris有ipaddrsel
命令。OSX正在遵循FreeBSD,在10.7中添加了ip6addrctl
。
编辑: RFC 3484排序存在一些问题,这些问题在这个额外的IETF草案文件中列出并参考:
https://datatracker.ietf.org/doc/html/draft-axu-addr-sel-01
例如,Solaris 为每个分配给物理接口的新地址创建新的别名接口。因此,if_index 还可以用于唯一地标识特定于源地址的路由表。其他操作系统的工作方式不同。作者喜欢 Solaris 的方法,为每个附加的 IPv6 接口提供一个新的别名,因此 eri0 将成为链路本地范围地址,必须指定 eri0:1 或 eri0:2 等才能使用全局范围地址。
显然,虽然这是一个好主意,但人们不能指望在相当长的时间内看到其他操作系统改变。