If you have a IPv6 enabled host that has more than one global-scope address, how can you programmatically identify the preferred address for bind()
?
如果您的IPv6主机具有多个全局范围地址,那么如何以编程方式识别bind()的首选地址?
Example address list:
示例地址列表:
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
On Solaris you can indicate a preferred address with an interface flag and it is available programmatically via SIOCGLIFCONF
:
在Solaris上,您可以使用接口标志指示首选地址,并通过SIOCGLIFCONF以编程方式提供:
/usr/include/net/if.h:
#define IFF_PREFERRED 0x0400000000 /* Prefer as source address */
As listed in the interface list:
如界面列表中所列:
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
This is not portable to OSX, Linux, FreeBSD, or Windows though. Windows is let off easy though as it has completely useless, from an administrators perspective, UUID based adapter names (depending upon the Windows version).
但这不适用于OSX,Linux,FreeBSD或Windows。从管理员的角度看,基于UUID的适配器名称(取决于Windows版本)完全没用,因此Windows很容易发布。
For Linux this article details how the parameter preferred_lft
, where lft
is short for "lifetime", can be altered to weight the selection process by the kernel. This setting doesn't appear conveniently available in the results of SIOCGIFCONF
or getifaddrs()
though.
对于Linux,本文详细介绍了如何更改参数preferred_lft,其中lft是“lifetime”的缩写,可以通过内核对选择过程进行加权。但是,在SIOCGIFCONF或getifaddrs()的结果中,此设置似乎不方便使用。
So I want to bind to eth0
, eri0
, or whatever available interface name. The choices are a bit stark:
所以我想绑定到eth0,eri0或任何可用的接口名称。选择有点鲜明:
- Fail on adapter names resolving to multiple interfaces. I take this approach for handling multicast transports (OpenPGM) as the protocol MUST have one-only sending address.
- 适配器名称解析为多个接口失败。我采用这种方法来处理多播传输(OpenPGM),因为协议必须只有一个发送地址。
- Bind to everything. This is a cop out and would be unexpected to users.
- 绑定一切。这是一个警察,对用户来说是意想不到的。
- Bind to the adapter with
SO_BINDTODEVICE
. This requiresCAP_NET_RAW
system capability on Linux which can be quite a cumbersome overhead for administrators. - 使用SO_BINDTODEVICE绑定到适配器。这需要Linux上的CAP_NET_RAW系统功能,这对管理员来说可能是一个非常麻烦的开销。
- Bind to the first IPv6 interface on the adapter. The ordering tends to be completely bogus.
- 绑定到适配器上的第一个IPv6接口。订购往往是完全虚假的。
- Bind to the last interface. David Croft's article implies Linux does this, but is also a bit bogus.
- 绑定到最后一个界面。 David Croft的文章暗示Linux会这样做,但也有点虚假。
- Enumerate over every interface and create a new socket explicitly for each.
- 枚举每个接口并为每个接口明确创建一个新套接字。
With option #6 I would expect you could usually be smarter and take the approach that if only a link-local scope address is available bind to that, otherwise bind to just the available global-link scope addresses.
使用选项#6,我希望你通常可以更聪明,并采取如果只有链接本地范围地址可用的方法绑定到该方法,否则只绑定到可用的全局链接范围地址。
When connecting to another host then RFC 3484 can be used, but as you can see all the choices are dependent upon matching the destination address:
当连接到另一台主机时,可以使用RFC 3484,但正如您所看到的,所有选择都取决于匹配目标地址:
- Prefer same address. (i.e. destination is local machine)
- 喜欢相同的地址。 (即目的地是本地机器)
- Prefer appropriate scope. (i.e. smallest scope shared with the destination)
- 喜欢适当的范围。 (即与目的地共享的最小范围)
- Avoid deprecated addresses.
- 避免弃用地址。
- Prefer home addresses. Prefer outgoing interface. (i.e. prefer an address on the interface we're sending out of)
- 喜欢家庭住址。首选外向接口。 (即更喜欢我们发送的接口上的地址)
- Prefer matching label.
- 喜欢匹配的标签。
- Prefer public addresses.
- 喜欢公共地址。
- Use longest matching prefix.
- 使用最长匹配前缀。
In some circumstances we can use #7 here, but in the interface example above both global-scope interfaces have a 64-bit prefix length.
在某些情况下,我们可以在这里使用#7,但在上面的接口示例中,两个全局范围接口都具有64位前缀长度。
RFC 3484 has the following pertinent lines:
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.IPv6寻址体系结构5允许将多个单播地址分配给接口。这些地址可能具有不同的可访问性范围(链接本地,站点本地或全局)。这些地址也可能是“首选”或“不赞成”6。
The link being to RFC 2462, similarly expanded:
链接到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.
首选地址 - 分配给接口的地址,上层协议的使用不受限制。优选地址可以用作从接口发送(或到接口)的分组的源(或目的地)地址。
But no methods to programmatically acquire this detail.
但没有方法以编程方式获取此细节。
Props to Win32 API that exposes an ioctl SIO_ADDRESS_LIST_SORT that allows a developer to use not only RFC 3484 sorting but to take into consideration any system administrator overrides. Linux has /etc/gai.conf
as used for RFC 3484 sorting in getaddrinfo()
but no API for directly accessing the sorting. Solaris has the ipaddrsel
command. OSX is following FreeBSD by adding ip6addrctl
in 10.7.
推文到Win32 API,公开ioctl SIO_ADDRESS_LIST_SORT,允许开发人员不仅使用RFC 3484排序,还要考虑任何系统管理员覆盖。 Linux有/etc/gai.conf用于getaddrinfo()中的RFC 3484排序,但没有用于直接访问排序的API。 Solaris具有ipaddrsel命令。 OSX通过在10.7中添加ip6addrctl来关注FreeBSD。
edit: Some concerns with RFC 3484 sorting are listed and referred to in this additional IETF draft document:
编辑:在此额外的IETF草案文档中列出并引用了一些关于RFC 3484排序的问题:
http://tools.ietf.org/html/draft-axu-addr-sel-01
http://tools.ietf.org/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为分配给物理接口的每个新地址创建新的别名接口。因此,if_index也可用于唯一标识该平台上的源地址特定路由表。其他操作系统的工作方式不同。
The author likes Solaris's approach of giving each additional IPv6 interface a new alias, so that eri0
would become the link-local scope address, and eri0:1
or eri0:2
, etc, must be specified to use a global-scope address.
作者喜欢Solaris为每个额外的IPv6接口提供新别名的方法,因此eri0将成为链接本地范围地址,并且必须指定eri0:1或eri0:2等使用全局范围地址。
Clearly whilst a nice idea one couldn't expect to see other OS change for quite some time.
显然,虽然一个不错的想法,人们不能指望在相当长的一段时间内看到其他操作系统的变化
1 个解决方案
#1
2
I'm not sure this is in the direction you're seeking, but...
我不确定这是你想要的方向,但......
Poking around in the iproute bundle's ip
code (ip/ipaddress.c
) under linux shows that the ip
command digs up interface flags like primary
and secondary
from a struct ifaddrmsg
, member ifa_flags
. The ifaddmsg
seems to be acquired through a struct nlmsghdr
which is documented in man 7 netlink
, and used via sendmsg
and recvmsg
interaction with the kernel, which overall sounds like a royal pain but it's at least programmatic. Whether primary and secondary would be enough to be useful is a separate question.
在linux下的iproute bundle的ip代码(ip / ipaddress.c)中查看,显示ip命令从结构ifaddrmsg,成员ifa_flags中挖掘出主要和次要的接口标志。 ifaddmsg似乎是通过一个结构nlmsghdr获得的,这个结构在man 7 netlink中有记录,并通过sendmsg和recvmsg与内核交互使用,这听起来像是一个皇家的痛苦但它至少是程序化的。初级和次级是否足够有用是一个单独的问题。
#1
2
I'm not sure this is in the direction you're seeking, but...
我不确定这是你想要的方向,但......
Poking around in the iproute bundle's ip
code (ip/ipaddress.c
) under linux shows that the ip
command digs up interface flags like primary
and secondary
from a struct ifaddrmsg
, member ifa_flags
. The ifaddmsg
seems to be acquired through a struct nlmsghdr
which is documented in man 7 netlink
, and used via sendmsg
and recvmsg
interaction with the kernel, which overall sounds like a royal pain but it's at least programmatic. Whether primary and secondary would be enough to be useful is a separate question.
在linux下的iproute bundle的ip代码(ip / ipaddress.c)中查看,显示ip命令从结构ifaddrmsg,成员ifa_flags中挖掘出主要和次要的接口标志。 ifaddmsg似乎是通过一个结构nlmsghdr获得的,这个结构在man 7 netlink中有记录,并通过sendmsg和recvmsg与内核交互使用,这听起来像是一个皇家的痛苦但它至少是程序化的。初级和次级是否足够有用是一个单独的问题。