如果您有一台启用了 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 上,您可以使用接口(interface)标志指示首选地址,并且可以通过 SIOCGLIFCONF
以编程方式使用该地址。 :/usr/include/net/if.h:
#define IFF_PREFERRED 0x0400000000 /* Prefer as source address */
如接口(interface)列表中所列: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
是“lifetime”的缩写,可以更改为内核对选择过程的权重。此设置在 SIOCGIFCONF
的结果中似乎不方便使用或 getifaddrs()
尽管。所以我想绑定(bind)到
eth0
, eri0
,或任何可用的接口(interface)名称。选择有点明显:SO_BINDTODEVICE
绑定(bind)到适配器.这需要 CAP_NET_RAW
Linux 上的系统功能,这对于管理员来说可能是相当麻烦的开销。 使用选项 #6,我希望您通常可以更聪明,并采取这种方法,如果只有链接本地范围地址可用,则绑定(bind)到该地址,否则仅绑定(bind)到可用的全局链接范围地址。
当连接到另一台主机时 RFC 3484可以使用,但如您所见,所有选择都取决于匹配目标地址:
界面。 (即更喜欢我们发送的接口(interface)上的地址
出)
在某些情况下,我们可以在这里使用 #7,但在上面的接口(interface)示例中,两个全局范围接口(interface)都有 64 位前缀长度。
RFC 3484有以下相关行:
The IPv6 addressing architecture 5 allows multiple unicast
addresses to be assigned to interfaces. These addresses may have
different reachability scopes (link-local, site-local, or global).
These addresses may also be "preferred" or "deprecated" 6.
链接指向 RFC 2462 ,同样展开:
preferred address - an address assigned to an interface whose use by upper layer protocols is unrestricted. Preferred addresses may be used as the source (or destination) address of packets sent from (or to) the interface.
但是没有方法以编程方式获取此详细信息。
公开 ioctl 的 Win32 API 的 Prop SIO_ADDRESS_LIST_SORT这使开发人员不仅可以使用 RFC 3484 排序,还可以考虑任何系统管理员覆盖。 Linux 有
/etc/gai.conf
用于 getaddrinfo()
中的 RFC 3484 排序但没有用于直接访问排序的 API。 Solaris 有 ipaddrsel
命令。 OSX 通过添加 ip6addrctl
来跟随 FreeBSD在 10.7。编辑:此附加 IETF 草案文档中列出并引用了与 RFC 3484 排序有关的一些问题:
https://datatracker.ietf.org/doc/html/draft-axu-addr-sel-01
Solaris, for example, creates new alias-interfaces for each new
address assigned to a physical interface. So if_index could also be
used to uniquely identify a source address specific routing table on
that platform. Other operating systems do not work the same way.
作者喜欢 Solaris 为每个额外的 IPv6 接口(interface)提供一个新别名的方法,以便
eri0
将成为链接本地作用域地址,而 eri0:1
或 eri0:2
等,必须指定使用全局范围的地址。显然,虽然这是一个好主意,但在很长一段时间内都无法期望看到其他操作系统发生变化。
最佳答案
我不确定这是否符合您正在寻找的方向,但是...
在 iproute 包的 ip
中闲逛linux下的代码( ip/ipaddress.c
)显示ip
命令挖掘接口(interface)标志,如 primary
和 secondary
来自 struct ifaddrmsg
, 成员(member) ifa_flags
. ifaddmsg
似乎是通过 struct nlmsghdr
获得的记录在 man 7 netlink
中,并通过 sendmsg
使用和 recvmsg
与内核的交互,这总体上听起来像是一种痛苦,但至少是程序化的。主要和次要是否足够有用是一个单独的问题。
关于linux - 确定适配器的首选 IPv6 源地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7202316/